From 16b3d9101c228aec0950e0a9240e157150057297 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 1 Jan 2025 09:11:15 -0500 Subject: [PATCH 01/93] devel release --- CMakeLists.txt | 2 +- Changes | 6 ++++++ configure.ac | 2 +- 3 files changed, 8 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 911751a8a..d53b2f664 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,7 +16,7 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project( Verilator - VERSION 5.032 + VERSION 5.033 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) diff --git a/Changes b/Changes index d2121e117..c7bafdf9a 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,12 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 5.033 devel +========================== + +**Minor:** + + Verilator 5.032 2025-01-01 ========================== diff --git a/configure.ac b/configure.ac index a7e1b8976..4420a1f7f 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.032 2025-01-01], +AC_INIT([Verilator],[5.033 devel], [https://verilator.org], [verilator],[https://verilator.org]) From f52155a7681baa5eaf741be20eba38ac20e809c0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 2 Jan 2025 06:21:30 -0500 Subject: [PATCH 02/93] Copyright year update. --- test_regress/t/TestVpiMain.cpp | 2 +- test_regress/t/t_EXAMPLE.py | 2 +- test_regress/t/t_EXAMPLE.v | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/test_regress/t/TestVpiMain.cpp b/test_regress/t/TestVpiMain.cpp index ab368cb24..3d3c4c4b6 100644 --- a/test_regress/t/TestVpiMain.cpp +++ b/test_regress/t/TestVpiMain.cpp @@ -1,7 +1,7 @@ // -*- mode: C++; c-file-style: "cc-mode" -*- //************************************************************************* // -// Copyright 2024 by Andrew Nolte. This program is free software; you can +// Copyright 2024-2025 by Andrew Nolte. 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. diff --git a/test_regress/t/t_EXAMPLE.py b/test_regress/t/t_EXAMPLE.py index d4f986441..f989a35fb 100755 --- a/test_regress/t/t_EXAMPLE.py +++ b/test_regress/t/t_EXAMPLE.py @@ -1,7 +1,7 @@ #!/usr/bin/env python3 # DESCRIPTION: Verilator: Verilog Test driver/expect definition # -# Copyright 2024 by Wilson Snyder. This program is free software; you +# Copyright 2025 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. diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index c3eb28a02..3dcd65512 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -13,7 +13,7 @@ // please note it here, otherwise:** // // This file ONLY is placed under the Creative Commons Public Domain, for -// any use, without warranty, 2024 by Wilson Snyder. +// any use, without warranty, 2025 by Wilson Snyder. // SPDX-License-Identifier: CC0-1.0 `define stop $stop From 3a69dd4a431ef384421e036a07e276f0356185b5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 2 Jan 2025 08:07:57 -0500 Subject: [PATCH 03/93] Commentary --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 850a53bfb..5b76dff64 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,7 +9,7 @@ assignees: '' Thanks for taking the time to report this. -Can you attach an example that shows the issue? (Must be openly licensed, ideally in test_regress format.) +Can you attach an example that shows the issue? (Must be openly licensed, completely self-contaned so can directly run what you provide. Ideally use [test_regress format](https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs).) What 'verilator' command line do we use to run your example? From 951f5eaa821c1ad6d9497db8a87b74ce877a2d70 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 2 Jan 2025 08:09:02 -0500 Subject: [PATCH 04/93] Commentary --- .github/ISSUE_TEMPLATE/bug_report.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md index 5b76dff64..968103dec 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.md +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -9,7 +9,7 @@ assignees: '' Thanks for taking the time to report this. -Can you attach an example that shows the issue? (Must be openly licensed, completely self-contaned so can directly run what you provide. Ideally use [test_regress format](https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs).) +Can you attach an example that shows the issue? (Must be openly licensed, completely self-contaned so can directly run what you provide. Ideally use test_regress format, see https://veripool.org/guide/latest/contributing.html?highlight=test_regress#reporting-bugs What 'verilator' command line do we use to run your example? From 010ae580b1e0bbd8004a6786c0c1602c2b0fa875 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Fri, 3 Jan 2025 10:33:29 +0000 Subject: [PATCH 05/93] Fix V3Simulate constant reuse (#5709) Use a generational allocator for reusing AstConst across V3Simulate::clear(), instead of using user1 (which is also used to store values of nodes). Also fix invalid lookup on array initializer --- src/V3Simulate.h | 73 ++++++++++++++---------------- test_regress/t/t_simulate_array.py | 18 ++++++++ test_regress/t/t_simulate_array.v | 24 ++++++++++ 3 files changed, 77 insertions(+), 38 deletions(-) create mode 100755 test_regress/t/t_simulate_array.py create mode 100644 test_regress/t/t_simulate_array.v diff --git a/src/V3Simulate.h b/src/V3Simulate.h index 4e727cd21..c98bfeb53 100644 --- a/src/V3Simulate.h +++ b/src/V3Simulate.h @@ -78,8 +78,7 @@ private: // Cleared on each always/assignw const VNUser1InUse m_inuser1; - // AstVar/AstVarScope::user1p() -> See AuxAstVar via m_varAux - // AstConst::user1() -> bool. This AstConst (allocated by this class) is in use + // AstNode::user1p() -> See AuxAstVar via m_varAux enum VarUsage : uint8_t { VU_NONE = 0, VU_LV = 1, VU_RV = 2, VU_LVDLY = 4 }; @@ -96,6 +95,32 @@ private: AstUser1Allocator m_varAux; + // We want to re-use allocated constants across calls to clear(), but we want to be able + // to 'clear()' fast, so we use a generation number based allocator. + struct ConstAllocator final { + size_t m_generation = 0; + size_t m_nextFree = 0; + std::deque m_constps; + AstConst* allocate(size_t currentGeneration, AstNode* nodep) { + if (m_generation != currentGeneration) { + m_generation = currentGeneration; + m_nextFree = 0; + } + UASSERT_OBJ(m_nextFree <= m_constps.size(), nodep, "Should only allocate at end"); + if (m_nextFree == m_constps.size()) { + m_constps.push_back( + new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()}); + } + AstConst* const constp = m_constps[m_nextFree++]; + constp->num().nodep(nodep); + return constp; + } + + ~ConstAllocator() { + for (AstConst* const constp : m_constps) VL_DO_DANGLING(delete constp, constp); + } + }; + // STATE // Major mode bool m_checkOnly; ///< Checking only (no simulation) mode @@ -113,8 +138,9 @@ private: int m_dataCount; ///< Bytes of data AstJumpGo* m_jumpp = nullptr; ///< Jump label we're branching from // Simulating: - std::unordered_map> - m_constps; ///< Lists of all AstConst* allocated per dtype + // Allocators for constants of various data types + std::unordered_map m_constps; + size_t m_constGeneration = 0; std::vector m_callStack; ///< Call stack for verbose error messages // Cleanup @@ -223,37 +249,8 @@ public: // Simulation METHODS private: AstConst* allocConst(AstNode* nodep) { - // Save time - kept a list of allocated but unused values - // It would be more efficient to do this by size, but the extra accounting - // slows things down more than we gain. - AstConst* constp; - // Grab free list corresponding to this dtype - std::deque& freeList = m_constps[nodep->dtypep()]; - bool allocNewConst = true; - if (!freeList.empty()) { - constp = freeList.front(); - if (!constp->user1()) { - // Front of free list is free, reuse it (otherwise allocate new node) - allocNewConst = false; // No need to allocate - // Mark the AstConst node as used, and move it to the back of the free list. This - // ensures that when all AstConst instances within the list are used, then the - // front of the list will be marked as used, in which case the enclosing 'if' will - // fail and we fall back to allocation. - constp->user1(1); - freeList.pop_front(); - freeList.push_back(constp); - // configure const - constp->num().nodep(nodep); - } - } - if (allocNewConst) { - // Need to allocate new constant - constp = new AstConst{nodep->fileline(), AstConst::DTyped{}, nodep->dtypep()}; - // Mark as in use, add to free list for later reuse - constp->user1(1); - freeList.push_back(constp); - } - return constp; + // Allocate a constant with this dtype. Reuse them across a 'clear()' call for efficiency. + return m_constps[nodep->dtypep()].allocate(m_constGeneration, nodep); } public: @@ -850,6 +847,8 @@ private: if (!itemp) { clearOptimizable(nodep, "Array initialization has too few elements, need element " + cvtToStr(offset)); + } else if (AstConst* const constp = VN_CAST(itemp, Const)) { + setValue(nodep, constp); } else { setValue(nodep, fetchValue(itemp)); } @@ -1256,6 +1255,7 @@ public: AstNode::user1ClearTree(); m_varAux.clear(); + ++m_constGeneration; } void mainTableCheck(AstNode* nodep) { setMode(true /*scoped*/, true /*checking*/, false /*params*/); @@ -1274,9 +1274,6 @@ public: mainGuts(nodep); } ~SimulateVisitor() override { - for (const auto& pair : m_constps) { - for (AstConst* const constp : pair.second) delete constp; - } m_constps.clear(); for (AstNode* ip : m_reclaimValuesp) delete ip; m_reclaimValuesp.clear(); diff --git a/test_regress/t/t_simulate_array.py b/test_regress/t/t_simulate_array.py new file mode 100755 index 000000000..15d1de923 --- /dev/null +++ b/test_regress/t/t_simulate_array.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile() + +test.execute(check_finished=True) + +test.passes() diff --git a/test_regress/t/t_simulate_array.v b/test_regress/t/t_simulate_array.v new file mode 100644 index 000000000..77c19e2ce --- /dev/null +++ b/test_regress/t/t_simulate_array.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +function integer fun; +integer array[0:0]; +begin + array[0] = 10; + fun = array[0]; +end +endfunction + +module test (); +begin + localparam something = fun(); + initial begin + if (something !== 10) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end +end +endmodule From 578d8d8391db3c85744b23f1584dc26fb541ccce Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D8=A3=D8=AD=D9=85=D8=AF=20=D8=A7=D9=84=D9=85=D8=AD=D9=85?= =?UTF-8?q?=D9=88=D8=AF=D9=8A?= <96682+aelmahmoudy@users.noreply.github.com> Date: Fri, 3 Jan 2025 16:58:33 +0200 Subject: [PATCH 06/93] Fix man pages whatis section (#5710) --- Makefile.in | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Makefile.in b/Makefile.in index ea0310369..a04a1f93d 100644 --- a/Makefile.in +++ b/Makefile.in @@ -201,7 +201,8 @@ verilator.1: ${srcdir}/bin/verilator verilator_coverage.1: ${srcdir}/bin/verilator_coverage pod2man $< $@ %.1: ${srcdir}/bin/% - help2man --no-info --no-discard-stderr --version-string=- $< -o $@ + help2man --no-info --no-discard-stderr --version-string=- \ + -n "$(shell $< --help | head -n 3 | tail -n 1)" $< -o $@ .PHONY: verilator.html verilator.html: From 19258ee18a9b5c321316dcdf64d53d062c32bb66 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jan 2025 10:00:56 -0500 Subject: [PATCH 07/93] Commentary --- Makefile.in | 2 +- bin/verilator | 3 +-- bin/verilator_gantt | 2 +- bin/verilator_profcfunc | 2 +- 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/Makefile.in b/Makefile.in index a04a1f93d..ec395ef6a 100644 --- a/Makefile.in +++ b/Makefile.in @@ -192,7 +192,7 @@ examples: all_nomsg # Docs .PHONY: docs -docs: info +docs: info $(VL_INST_MAN_FILES) info: $(INFOS) diff --git a/bin/verilator b/bin/verilator index 6f8f5ae35..769f3fb03 100755 --- a/bin/verilator +++ b/bin/verilator @@ -269,8 +269,7 @@ __END__ =head1 NAME -Verilator - Translate and simulate SystemVerilog code using C++/SystemC - +Verilator - Lint, compile and simulate SystemVerilog code using C++/SystemC =head1 SYNOPSIS diff --git a/bin/verilator_gantt b/bin/verilator_gantt index 2f5fb5ec1..94f52870d 100755 --- a/bin/verilator_gantt +++ b/bin/verilator_gantt @@ -521,7 +521,7 @@ def write_vcd(filename): parser = argparse.ArgumentParser( allow_abbrev=False, formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Create Gantt chart of multi-threaded execution. + description="""Create Gantt chart of Verilator multi-threaded execution Verilator_gantt creates a visual representation to help analyze Verilator #xmultithreaded simulation performance, by showing when each macro-task diff --git a/bin/verilator_profcfunc b/bin/verilator_profcfunc index 434fe2823..60c5a428a 100755 --- a/bin/verilator_profcfunc +++ b/bin/verilator_profcfunc @@ -164,7 +164,7 @@ def profcfunc(filename): parser = argparse.ArgumentParser( allow_abbrev=False, formatter_class=argparse.RawDescriptionHelpFormatter, - description="""Read gprof report created with --prof-cfuncs. + description="""Read gprof report created with Verilator --prof-cfuncs Verilator_profcfunc reads a profile report created by gprof. The names of the functions are then transformed, assuming the user used Verilator's From 036c1fb0f9a5ccbdc2dbd1e073561ae52d121ef7 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jan 2025 10:02:48 -0500 Subject: [PATCH 08/93] Commentary: Changes update --- Changes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changes b/Changes index c7bafdf9a..94876c00b 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,9 @@ Verilator 5.033 devel **Minor:** +* Fix V3Simulate constant reuse (#5709). [Geza Lore] +* Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] + Verilator 5.032 2025-01-01 ========================== From 8ca49d98c1c9d22795e9925e7b4ee2b622f073af Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jan 2025 19:15:03 -0500 Subject: [PATCH 09/93] Fix %p format output for real inside struct (#5713). --- Changes | 1 + include/verilated.cpp | 2 +- test_regress/t/t_class_format.out | 4 ++-- test_regress/t/t_class_format.v | 2 ++ 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/Changes b/Changes index 94876c00b..64d01dec6 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.033 devel * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] +* Fix %p format output for real inside struct (#5713). Verilator 5.032 2025-01-01 diff --git a/include/verilated.cpp b/include/verilated.cpp index 15f3b10a9..a99d97dee 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -1898,7 +1898,7 @@ std::string VL_TO_STRING(CData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 8, lhs std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 16, lhs); } std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 32, lhs); } std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 64, lhs); } -std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%d", 0, 64, lhs); } +std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%g", 0, 64, lhs); } std::string VL_TO_STRING_W(int words, const WDataInP obj) { return VL_SFORMATF_N_NX("'h%0x", 0, words * VL_EDATASIZE, obj); } diff --git a/test_regress/t/t_class_format.out b/test_regress/t/t_class_format.out index dd9e17d2a..4fef2ba25 100644 --- a/test_regress/t/t_class_format.out +++ b/test_regress/t/t_class_format.out @@ -1,4 +1,4 @@ -''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name"}' -''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name"}' +''{b:'h1, i:'h2a, carray4:'{'h11, 'h22, 'h33, 'h44} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' +''{b:'h1, i:'h2a, carray4:'{'h911, 'h922, 'h933, 'h944} , cwide:'{'h0, 'h0} , name:"object_name", r:2.2}' DEBUG: object_name (@0) message *-* All Finished *-* diff --git a/test_regress/t/t_class_format.v b/test_regress/t/t_class_format.v index ac11b68c0..8075e5bc0 100644 --- a/test_regress/t/t_class_format.v +++ b/test_regress/t/t_class_format.v @@ -17,6 +17,7 @@ class Cls; bit [15:0] carray4 [4]; bit [64:0] cwide[2]; string name; + real r; task debug(); $display("DEBUG: %s (@%0t) %s", this.name, $realtime, "message"); endtask @@ -28,6 +29,7 @@ module t (/*AUTOARG*/); c = new; c.b = '1; c.i = 42; + c.r = 2.2; c.name = "object_name"; c.carray4[0] = 16'h11; From 1d0563212e274ba9b8a3af3e521958e2fc742f42 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 3 Jan 2025 19:39:48 -0500 Subject: [PATCH 10/93] Fix pattern assignment to real inside struct (#5713). --- Changes | 1 + src/V3Width.cpp | 2 +- test_regress/t/t_json_only_tag.out | 8 ++++---- test_regress/t/t_struct_pat.v | 10 ++++++++++ 4 files changed, 16 insertions(+), 5 deletions(-) diff --git a/Changes b/Changes index 64d01dec6..4ec85df3b 100644 --- a/Changes +++ b/Changes @@ -15,6 +15,7 @@ Verilator 5.033 devel * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] +* Fix pattern assignment to real inside struct (#5713). * Fix %p format output for real inside struct (#5713). diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 969209609..652dd01e7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -2874,8 +2874,8 @@ class WidthVisitor final : public VNVisitor { if (nodep->didWidthAndSet()) return; // This node is a dtype & not both PRELIMed+FINALed // Iterate into subDTypep() to resolve that type and update pointer. nodep->refDTypep(iterateEditMoveDTypep(nodep, nodep->subDTypep())); - nodep->dtypep(nodep); // The member itself, not subDtype nodep->widthFromSub(nodep->subDTypep()); + nodep->dtypeFrom(nodep->subDTypep()); if (nodep->valuep()) { userIterateAndNext(nodep->valuep(), WidthVP{nodep->dtypep(), PRELIM}.p()); iterateCheckAssign(nodep, "Initial value", nodep->valuep(), FINAL, nodep->dtypep()); diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index cb03e77cd..fe4fb9b92 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -69,10 +69,10 @@ {"type":"BASICDTYPE","name":"logic","addr":"(RB)","loc":"d,24:7,24:12","dtypep":"(RB)","keyword":"logic","generic":false,"rangep": []}, {"type":"STRUCTDTYPE","name":"m.my_struct","addr":"(K)","loc":"d,20:12,20:18","dtypep":"(K)","packed":true,"isFourstate":true,"generic":false,"classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(SB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(TB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(UB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(VB)","generic":false,"childDTypep": [],"valuep": []} + {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","generic":false,"childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","generic":false,"childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","generic":false,"childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","generic":false,"childDTypep": [],"valuep": []} ]}, {"type":"IFACEREFDTYPE","name":"","addr":"(O)","loc":"d,29:8,29:12","dtypep":"(O)","isPortDecl":false,"isVirtual":false,"cellName":"itop","ifaceName":"ifc","modportName":"","generic":false,"ifacep":"UNLINKED","cellp":"(L)","modportp":"UNLINKED","paramsp": []}, {"type":"BASICDTYPE","name":"logic","addr":"(S)","loc":"d,31:27,31:28","dtypep":"(S)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, diff --git a/test_regress/t/t_struct_pat.v b/test_regress/t/t_struct_pat.v index 931113fc0..7b9660935 100644 --- a/test_regress/t/t_struct_pat.v +++ b/test_regress/t/t_struct_pat.v @@ -31,8 +31,14 @@ module t(/*AUTOARG*/); bit [3:0][31:0] b4; } sab4p_t; + typedef struct { + int i; + real r; + } sir_t; + sab4u_t ab4u[2][3]; sab4p_t ab4p[2][3]; + sir_t sir; initial begin abcp = '{1, 2, 3}; @@ -96,6 +102,10 @@ module t(/*AUTOARG*/); if (ab4u[1][2].b4[2] !== 20) $stop; if (ab4u[1][2].b4[3] !== 30) $stop; + sir = '{1, 2.2}; + if (sir.i !== 1) $stop; + if (sir.r !== 2.2) $stop; + $write("*-* All Finished *-*\n"); $finish; end From b531001f55ac01d57686a60ac8eeca476c833836 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 4 Jan 2025 10:36:34 -0500 Subject: [PATCH 11/93] Fix segfault when only enum value referenced in package (#5714). --- Changes | 1 + src/V3Dead.cpp | 24 +++++++++++++++++++----- test_regress/t/t_debug_emitv.out | 3 +++ test_regress/t/t_lint_importstar_bad.v | 2 +- test_regress/t/t_opt_dead_enumpkg.py | 18 ++++++++++++++++++ test_regress/t/t_opt_dead_enumpkg.v | 22 ++++++++++++++++++++++ 6 files changed, 64 insertions(+), 6 deletions(-) create mode 100755 test_regress/t/t_opt_dead_enumpkg.py create mode 100644 test_regress/t/t_opt_dead_enumpkg.v diff --git a/Changes b/Changes index 4ec85df3b..815da1703 100644 --- a/Changes +++ b/Changes @@ -17,6 +17,7 @@ Verilator 5.033 devel * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] * Fix pattern assignment to real inside struct (#5713). * Fix %p format output for real inside struct (#5713). +* Fix segfault when only enum value referenced in package (#5714). [Dan Katz] Verilator 5.032 2025-01-01 diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index d96a7d54c..fedf41048 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -62,7 +62,8 @@ class DeadVisitor final : public VNVisitor { const bool m_elimCells; // Allow removal of Cells // List of all encountered to avoid another loop through tree std::vector m_varsp; - std::vector m_dtypesp; + std::vector m_dtypeElimsp; // Data types might eliminate + std::map m_dtypePkgsp; // Data type's containing package std::vector m_vscsp; std::vector m_scopesp; std::vector m_cellsp; @@ -73,6 +74,7 @@ class DeadVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) bool m_inAssign = false; // Currently in an assign + AstNodeDType* m_curDTypep = nullptr; // Current NodeDType AstNodeModule* m_modp = nullptr; // Current module AstSelLoopVars* m_selloopvarsp = nullptr; // Current loop vars @@ -84,8 +86,11 @@ class DeadVisitor final : public VNVisitor { } void checkAll(AstNode* nodep) { - if (nodep != nodep->dtypep()) { // NodeDTypes reference themselves - if (AstNode* const subnodep = nodep->dtypep()) subnodep->user1Inc(); + if (AstNode* const subnodep = nodep->dtypep()) { + if (nodep != subnodep // Not NodeDTypes reference themselves + && m_curDTypep != subnodep) { // Not EnumItem referencing parent Enum + subnodep->user1Inc(); + } } if (AstNode* const subnodep = nodep->getChildDTypep()) subnodep->user1Inc(); } @@ -98,8 +103,9 @@ class DeadVisitor final : public VNVisitor { && !VN_IS(nodep, MemberDType) // Keep member names iff upper type exists && !nodep->undead() // VoidDType or something Netlist points to ) { - m_dtypesp.push_back(nodep); + m_dtypeElimsp.push_back(nodep); } + if (VN_IS(m_modp, Package) || VN_IS(m_modp, Class)) m_dtypePkgsp.emplace(nodep, m_modp); if (AstNode* const subnodep = nodep->virtRefDTypep()) subnodep->user1Inc(); if (AstNode* const subnodep = nodep->virtRefDType2p()) subnodep->user1Inc(); } @@ -212,6 +218,8 @@ class DeadVisitor final : public VNVisitor { if (nodep->ifaceViaCellp()) nodep->ifaceViaCellp()->user1Inc(); } void visit(AstNodeDType* nodep) override { + VL_RESTORER(m_curDTypep); + m_curDTypep = nodep; iterateChildren(nodep); checkDType(nodep); checkAll(nodep); @@ -442,7 +450,8 @@ class DeadVisitor final : public VNVisitor { } } } - for (std::vector::iterator it = m_dtypesp.begin(); it != m_dtypesp.end(); ++it) { + for (std::vector::iterator it = m_dtypeElimsp.begin(); it != m_dtypeElimsp.end(); + ++it) { if ((*it)->user1() == 0) { // It's possible that there if a reference to each individual member, but // not to the dtype itself. Check and don't remove the parent dtype if @@ -517,6 +526,11 @@ public: vscp->varp()->user1Inc(); } + // If data type has a reference in another package, then keep defining package around + for (auto& itr : m_dtypePkgsp) { + if (itr.first->user1()) itr.second->user1Inc(); + } + deadCheckTypedefs(); deadCheckVar(); // We only eliminate scopes when in a flattened structure diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 689bab73a..20e344b0f 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -341,6 +341,9 @@ module Vt_debug_emitv_sub; endfunction real signed r; endmodule +package Vt_debug_emitv_p; + logic pkgvar; +endpackage package Vt_debug_emitv_Pkg; logic signed [31:0] PKG_PARAM; endpackage diff --git a/test_regress/t/t_lint_importstar_bad.v b/test_regress/t/t_lint_importstar_bad.v index d61e12f78..752d64a64 100644 --- a/test_regress/t/t_lint_importstar_bad.v +++ b/test_regress/t/t_lint_importstar_bad.v @@ -5,7 +5,7 @@ // SPDX-License-Identifier: CC0-1.0 package defs; - int sig; + localparam PAR = 1; endpackage import defs::*; diff --git a/test_regress/t/t_opt_dead_enumpkg.py b/test_regress/t/t_opt_dead_enumpkg.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_opt_dead_enumpkg.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_opt_dead_enumpkg.v b/test_regress/t/t_opt_dead_enumpkg.v new file mode 100644 index 000000000..491e1ad5a --- /dev/null +++ b/test_regress/t/t_opt_dead_enumpkg.v @@ -0,0 +1,22 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +package pkg; + typedef enum logic [2:0] { + TWO = 2, + THREE = 3 + } enum_t; +endpackage + +module t; + localparam L_TWO = pkg::TWO; + initial begin + if (L_TWO != 2) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 88dd95980aaddb989365c8ec93b42555694a199d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 4 Jan 2025 12:50:55 -0500 Subject: [PATCH 12/93] Internal cleanups, remove extra iterate, but no functional change intended --- src/V3Width.cpp | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 652dd01e7..0c9a39299 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4223,9 +4223,8 @@ class WidthVisitor final : public VNVisitor { UASSERT_OBJ(classp, nodep, "Unlinked classOrPackagep()"); UASSERT_OBJ(nodep->taskp(), nodep, "Unlinked taskp()"); } - userIterate(nodep->taskp(), nullptr); - if (!assign) nodep->dtypeFrom(nodep->taskp()); processFTaskRefArgs(nodep); + if (!assign) nodep->dtypeFrom(nodep->taskp()); } void visit(AstNewCopy* nodep) override { if (nodep->didWidthAndSet()) return; @@ -6086,7 +6085,6 @@ class WidthVisitor final : public VNVisitor { nodep->v3error("Cannot call non-static member function " << nodep->prettyNameQ() << " without object (IEEE 1800-2023 8.10)"); } - userIterate(nodep->taskp(), nullptr); // And do the arguments to the task/function too processFTaskRefArgs(nodep); nodep->addPinsp(withp); @@ -6094,9 +6092,9 @@ class WidthVisitor final : public VNVisitor { } void visit(AstNodeProcedure* nodep) override { assertAtStatement(nodep); + VL_RESTORER(m_procedurep); m_procedurep = nodep; userIterateChildren(nodep, nullptr); - m_procedurep = nullptr; } void visit(AstSenItem* nodep) override { UASSERT_OBJ(nodep->isClocked(), nodep, "Invalid edge"); From 78d6ec8114ee3e270b76a4db96920cc3e73333f0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 4 Jan 2025 12:55:15 -0500 Subject: [PATCH 13/93] Fix error message when call task as a function (#3089). --- Changes | 1 + src/V3LinkDot.cpp | 6 -- src/V3Width.cpp | 33 +++++++-- test_regress/t/t_class_func_nvoid_bad.out | 25 +++++++ test_regress/t/t_class_func_nvoid_bad.py | 16 +++++ test_regress/t/t_class_func_nvoid_bad.v | 76 +++++++++++++++++++++ test_regress/t/t_func_task_bad.out | 7 +- test_regress/t/t_func_void_bad.out | 11 +-- test_regress/t/t_func_void_bad.v | 4 ++ test_regress/t/t_lint_implicit_func_bad.out | 3 +- 10 files changed, 165 insertions(+), 17 deletions(-) create mode 100644 test_regress/t/t_class_func_nvoid_bad.out create mode 100755 test_regress/t/t_class_func_nvoid_bad.py create mode 100644 test_regress/t/t_class_func_nvoid_bad.v diff --git a/Changes b/Changes index 815da1703..fc1016325 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 5.033 devel **Minor:** +* Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] * Fix pattern assignment to real inside struct (#5713). diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index c747239f4..8c54b2984 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -2336,11 +2336,6 @@ class LinkDotResolveVisitor final : public VNVisitor { nodep->addStmtsp(superNewStmtp); return superNewStmtp; } - void taskFuncSwapCheck(AstNodeFTaskRef* nodep) { - if (nodep->taskp() && VN_IS(nodep->taskp(), Task) && VN_IS(nodep, FuncRef)) { - nodep->v3error("Illegal call of a task as a function: " << nodep->prettyNameQ()); - } - } void checkNoDot(AstNode* nodep) { if (VL_UNLIKELY(m_ds.m_dotPos != DP_NONE)) { // UINFO(9, indent() << "ds=" << m_ds.ascii() << endl); @@ -3779,7 +3774,6 @@ class LinkDotResolveVisitor final : public VNVisitor { okSymp->cellErrorScopes(nodep); } } - taskFuncSwapCheck(nodep); } } void visit(AstSelBit* nodep) override { diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 0c9a39299..0dca57d26 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -3929,11 +3929,14 @@ class WidthVisitor final : public VNVisitor { AstNodeExpr* argsp = nullptr; if (nodep->pinsp()) argsp = nodep->pinsp()->unlinkFrBackWithNext(); AstNodeFTaskRef* newp = nullptr; - if (VN_IS(ftaskp, Task)) { - newp = new AstTaskRef{nodep->fileline(), VN_AS(ftaskp, Task), argsp}; + // We use m_vup to determine task or function, so that later error checks + // for funcref->task and taskref->func will pick up properly + if (!m_vup) { // Called as task + newp = new AstTaskRef{nodep->fileline(), nodep->name(), argsp}; } else { - newp = new AstFuncRef{nodep->fileline(), VN_AS(ftaskp, Func), argsp}; + newp = new AstFuncRef{nodep->fileline(), nodep->name(), argsp}; } + newp->taskp(ftaskp); // Not passed above as might be func vs task mismatch newp->classOrPackagep(classp); nodep->replaceWith(newp); VL_DO_DANGLING(nodep->deleteTree(), nodep); @@ -3941,7 +3944,18 @@ class WidthVisitor final : public VNVisitor { nodep->taskp(ftaskp); nodep->dtypeFrom(ftaskp); nodep->classOrPackagep(classp); - if (VN_IS(ftaskp, Task)) nodep->dtypeSetVoid(); + if (VN_IS(ftaskp, Task)) { + if (!m_vup) { + nodep->dtypeSetVoid(); + } else { + if (m_vup->prelim()) { + nodep->v3error( + "Cannot call a task/void-function as a member function: " + << nodep->prettyNameQ()); + } + nodep->dtypeSetVoid(); + } + } if (withp) nodep->addPinsp(withp); processFTaskRefArgs(nodep); } @@ -5824,7 +5838,15 @@ class WidthVisitor final : public VNVisitor { } void visit(AstFuncRef* nodep) override { visit(static_cast(nodep)); - nodep->dtypeFrom(nodep->taskp()); + if (nodep->taskp() && VN_IS(nodep->taskp(), Task)) { + UASSERT_OBJ(m_vup, nodep, "Function reference where widthed expression expection"); + if (m_vup->prelim()) + nodep->v3error( + "Cannot call a task/void-function as a function: " << nodep->prettyNameQ()); + nodep->dtypeSetVoid(); + } else { + nodep->dtypeFrom(nodep->taskp()); + } if (nodep->fileline()->timingOn()) { AstNodeModule* const classp = nodep->classOrPackagep(); if (nodep->name() == "self" && classp->name() == "process") { @@ -6089,6 +6111,7 @@ class WidthVisitor final : public VNVisitor { processFTaskRefArgs(nodep); nodep->addPinsp(withp); nodep->didWidth(true); + // See steps that follow in visit(AstFuncRef*) } void visit(AstNodeProcedure* nodep) override { assertAtStatement(nodep); diff --git a/test_regress/t/t_class_func_nvoid_bad.out b/test_regress/t/t_class_func_nvoid_bad.out new file mode 100644 index 000000000..52690482f --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.out @@ -0,0 +1,25 @@ +%Error: t/t_class_func_nvoid_bad.v:47:11: Cannot call a task/void-function as a function: 'mod_fv' + : ... note: In instance 't' + 47 | if (mod_fv() == 10) $stop; + | ^~~~~~ +%Error: t/t_class_func_nvoid_bad.v:50:11: Cannot call a task/void-function as a function: 'mod_t' + : ... note: In instance 't' + 50 | if (mod_t() == 10) $stop; + | ^~~~~ +%Error: t/t_class_func_nvoid_bad.v:58:13: Cannot call a task/void-function as a member function: 'fv' + : ... note: In instance 't' + 58 | if (c.fv() == 10) $stop; + | ^~ +%Error: t/t_class_func_nvoid_bad.v:61:13: Cannot call a task/void-function as a member function: 't' + : ... note: In instance 't' + 61 | if (c.t() == 10) $stop; + | ^ +%Error: t/t_class_func_nvoid_bad.v:69:13: Cannot call a task/void-function as a function: 'sfv' + : ... note: In instance 't' + 69 | if (c.sfv() == 10) $stop; + | ^~~ +%Error: t/t_class_func_nvoid_bad.v:72:13: Cannot call a task/void-function as a function: 'st' + : ... note: In instance 't' + 72 | if (c.st() == 10) $stop; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_func_nvoid_bad.py b/test_regress/t/t_class_func_nvoid_bad.py new file mode 100755 index 000000000..31228c9a7 --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.py @@ -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() diff --git a/test_regress/t/t_class_func_nvoid_bad.v b/test_regress/t/t_class_func_nvoid_bad.v new file mode 100644 index 000000000..f8180e69b --- /dev/null +++ b/test_regress/t/t_class_func_nvoid_bad.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2020 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + + +class Cls; + function int fi(); + return 10; + endfunction + function void fv(); + endfunction + task t(); + endtask + static function int sfi(); + return 10; + endfunction + static function void sfv(); + endfunction + static task st(); + endtask +endclass + +module t (/*AUTOARG*/); + + function int mod_fi(); + return 10; + endfunction + function void mod_fv(); + endfunction + task mod_t(); + endtask + + initial begin + Cls c; + c = new; + + // For test of calling function in void context, see t_func_void_bad.v + + // Module + if (mod_fi() != 10) $stop; // OK + void'(mod_fi()); // OK + + mod_fv(); // Warn IGNOREDRETURN + void'(mod_fv()); // OK + if (mod_fv() == 10) $stop; // Bad call of task as function + + mod_t(); // OK + if (mod_t() == 10) $stop; // Bad call of task as function + + // Member functions + if (c.fi() != 10) $stop; // OK + void'(c.fi()); // OK + + c.fv(); // Ok + void'(c.fv()); // OK + if (c.fv() == 10) $stop; // Bad + + c.t(); // OK + if (c.t() == 10) $stop; // Bad + + // Static member functions + if (c.sfi() != 10) $stop; // OK + void'(c.sfi()); // OK + + c.sfv(); // Ok + void'(c.sfv()); // OK + if (c.sfv() == 10) $stop; // Bad + + c.st(); // OK + if (c.st() == 10) $stop; // Bad + + $stop; + end +endmodule diff --git a/test_regress/t/t_func_task_bad.out b/test_regress/t/t_func_task_bad.out index 5a85eb04d..3cd03ead0 100644 --- a/test_regress/t/t_func_task_bad.out +++ b/test_regress/t/t_func_task_bad.out @@ -1,4 +1,9 @@ -%Error: t/t_func_task_bad.v:10:11: Illegal call of a task as a function: 'task_as_func' +%Error: t/t_func_task_bad.v:10:11: Cannot call a task/void-function as a function: 'task_as_func' + : ... note: In instance 't' 10 | if (task_as_func(1'b0)) $stop; | ^~~~~~~~~~~~ +%Error: t/t_func_task_bad.v:10:7: Logical operator IF expects a non-complex data type on the If. + : ... note: In instance 't' + 10 | if (task_as_func(1'b0)) $stop; + | ^~ %Error: Exiting due to diff --git a/test_regress/t/t_func_void_bad.out b/test_regress/t/t_func_void_bad.out index e14223a08..37b0f4cb6 100644 --- a/test_regress/t/t_func_void_bad.out +++ b/test_regress/t/t_func_void_bad.out @@ -1,9 +1,12 @@ -%Warning-IGNOREDRETURN: t/t_func_void_bad.v:21:7: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) - 21 | f1(); +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:24:7: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 24 | f1(); | ^~ ... For warning description see https://verilator.org/warn/IGNOREDRETURN?v=latest ... Use "/* verilator lint_off IGNOREDRETURN */" and lint_on around source to disable this message. -%Warning-IGNOREDRETURN: t/t_func_void_bad.v:24:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) - 24 | c.fi(); +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:27:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 27 | c.fi(); | ^~ +%Warning-IGNOREDRETURN: t/t_func_void_bad.v:28:9: Ignoring return value of non-void function (IEEE 1800-2023 13.4.1) + 28 | c.sfi(); + | ^~~ %Error: Exiting due to diff --git a/test_regress/t/t_func_void_bad.v b/test_regress/t/t_func_void_bad.v index 837595812..e9552e58c 100644 --- a/test_regress/t/t_func_void_bad.v +++ b/test_regress/t/t_func_void_bad.v @@ -8,6 +8,9 @@ class Cls; function int fi(); return 10; endfunction + static function int sfi(); + return 10; + endfunction endclass module t; @@ -22,6 +25,7 @@ module t; // c = new; c.fi(); // Bad - ignored result + c.sfi(); // Bad - ignored result // $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_lint_implicit_func_bad.out b/test_regress/t/t_lint_implicit_func_bad.out index 6d790a04c..78301f0c4 100644 --- a/test_regress/t/t_lint_implicit_func_bad.out +++ b/test_regress/t/t_lint_implicit_func_bad.out @@ -1,4 +1,5 @@ -%Error: t/t_lint_implicit_func_bad.v:12:11: Illegal call of a task as a function: 'imp_func_conflict' +%Error: t/t_lint_implicit_func_bad.v:12:11: Cannot call a task/void-function as a function: 'imp_func_conflict' + : ... note: In instance 't' 12 | assign imp_func_conflict = 1'b1; | ^~~~~~~~~~~~~~~~~ %Error: Exiting due to From b6a400ee9b6b0966d04696787f649cfe3974153c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 5 Jan 2025 17:10:04 -0500 Subject: [PATCH 14/93] Support generated classes (#5665). --- Changes | 1 + src/V3Begin.cpp | 12 +++++++ test_regress/t/t_EXAMPLE.v | 3 +- test_regress/t/t_gen_class.py | 18 ++++++++++ test_regress/t/t_gen_class.v | 67 +++++++++++++++++++++++++++++++++++ 5 files changed, 100 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_gen_class.py create mode 100644 test_regress/t/t_gen_class.v diff --git a/Changes b/Changes index fc1016325..aac6d50ff 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 5.033 devel **Minor:** +* Support generated classes (#5665). [Shou-Li Hsu] * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] diff --git a/src/V3Begin.cpp b/src/V3Begin.cpp index 4817bd0f1..f07de3594 100644 --- a/src/V3Begin.cpp +++ b/src/V3Begin.cpp @@ -148,6 +148,18 @@ class BeginVisitor final : public VNVisitor { void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); m_modp = nodep; + // Rename it (e.g. class under a generate) + if (m_unnamedScope != "") { + nodep->name(dot(m_unnamedScope, nodep->name())); + UINFO(8, " rename to " << nodep->name() << endl); + m_statep->userMarkChanged(nodep); + } + VL_RESTORER(m_displayScope); + VL_RESTORER(m_namedScope); + VL_RESTORER(m_unnamedScope); + m_displayScope = ""; + m_namedScope = ""; + m_unnamedScope = ""; iterateChildren(nodep); } void visit(AstNodeFTask* nodep) override { diff --git a/test_regress/t/t_EXAMPLE.v b/test_regress/t/t_EXAMPLE.v index 3dcd65512..b00361778 100644 --- a/test_regress/t/t_EXAMPLE.v +++ b/test_regress/t/t_EXAMPLE.v @@ -17,7 +17,8 @@ // SPDX-License-Identifier: CC0-1.0 `define stop $stop -`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"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); module t(/*AUTOARG*/ // Inputs diff --git a/test_regress/t/t_gen_class.py b/test_regress/t/t_gen_class.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_gen_class.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_gen_class.v b/test_regress/t/t_gen_class.v new file mode 100644 index 000000000..58628ba58 --- /dev/null +++ b/test_regress/t/t_gen_class.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0x exp=%0x (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"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); + +module Child; + int ch_value; +endmodule + +module Parent; + for (genvar i = 0; i < 10; i++) begin : gen_child + Child child(); + end +endmodule + +module t; + Parent parent(); + + virtual class ChildAgentBase; + pure virtual task preload(int value); + pure virtual function string name(); + endclass + + ChildAgentBase child_agents[10]; + + for (genvar i = 0; i < 10; i++) begin : gfor + class ChildAgent extends ChildAgentBase; + task automatic preload(int value); + parent.gen_child[i].child.ch_value = value; + endtask + function string name(); + return $sformatf("%m"); + endfunction + endclass + + ChildAgent agent = new(); + + initial child_agents[i] = agent; + end + + task automatic preload_children; + for (int i = 0; i < 10; i++) begin + child_agents[i].preload(i); + end + endtask + + string s; + + initial begin + #1; // Ensure all class instances are initialized + preload_children(); + `checkh(parent.gen_child[3].child.ch_value, 3); + `checkh(parent.gen_child[8].child.ch_value, 8); +`ifdef VERILATOR + // Some legal examples "t.gfor[4].\ChildAgent::name", "t.gfor[4].ChildAgent.name" + `checks(child_agents[4].name(), "t.gfor[4].ChildAgent.name"); +`endif + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 76b2ac9cc1dbc261068518d289d5512e465fb312 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 5 Jan 2025 19:30:39 -0500 Subject: [PATCH 15/93] Support `+incdir` with multiple directories. --- Changes | 1 + docs/guide/exe_verilator.rst | 5 ++++- src/V3Options.cpp | 11 +++++++++-- test_regress/t/t_flag_incdir.py | 16 ++++++++++++++++ test_regress/t/t_flag_incdir.v | 14 ++++++++++++++ 5 files changed, 44 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_flag_incdir.py create mode 100644 test_regress/t/t_flag_incdir.v diff --git a/Changes b/Changes index aac6d50ff..8254e2147 100644 --- a/Changes +++ b/Changes @@ -14,6 +14,7 @@ Verilator 5.033 devel **Minor:** * Support generated classes (#5665). [Shou-Li Hsu] +* Support `+incdir` with multiple directories. * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 7afdb5df6..1f653d652 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -751,7 +751,10 @@ Summary: .. option:: +incdir+ - See :vlopt:`-y`. + See :vlopt:`-y`. Unlike with :vlopt:`-y`, multiple directories may be + specified separated with a `+` symbol; this is for Verilog-XL + compatibility and is not recommended usage as this is not supported by + some third-party tools. .. option:: --inline-mult diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 7a7714b1e..5e78fd40e 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1127,8 +1127,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, // Plus options DECL_OPTION("+define+", CbPartialMatch, [this](const char* optp) VL_MT_DISABLED { addDefine(optp, true); }); - DECL_OPTION("+incdir+", CbPartialMatch, - [this, &optdir](const char* optp) { addIncDirUser(parseFileArg(optdir, optp)); }); + DECL_OPTION("+incdir+", CbPartialMatch, [this, &optdir](const char* optp) { + string dirs = optp; + string::size_type pos; + while ((pos = dirs.find('+')) != string::npos) { + addIncDirUser(parseFileArg(optdir, dirs.substr(0, pos))); + dirs = dirs.substr(pos + 1); + } + addIncDirUser(parseFileArg(optdir, dirs)); + }); DECL_OPTION("+libext+", CbPartialMatch, [this](const char* optp) { string exts = optp; string::size_type pos; diff --git a/test_regress/t/t_flag_incdir.py b/test_regress/t/t_flag_incdir.py new file mode 100755 index 000000000..a4f25aabb --- /dev/null +++ b/test_regress/t/t_flag_incdir.py @@ -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('vlt') + +test.lint(verilator_flags2=['+incdir+ignore1+t/tsub+ignore2']) + +test.passes() diff --git a/test_regress/t/t_flag_incdir.v b/test_regress/t/t_flag_incdir.v new file mode 100644 index 000000000..7fbf7d202 --- /dev/null +++ b/test_regress/t/t_flag_incdir.v @@ -0,0 +1,14 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`include "t_flag_f_tsub_inc.v" + +`ifndef GOT_DEF5 +`error "No GOT_DEF5" +`endif + +module t (/*AUTOARG*/); +endmodule From dcaf6476e8419433939f7f91f045ae74a2c5965a Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sun, 5 Jan 2025 22:52:29 -0500 Subject: [PATCH 16/93] Fix matching language extension options including dots. --- Changes | 1 + src/V3Options.cpp | 6 ++++-- test_regress/t/t_langext_1d.py | 18 ++++++++++++++++++ test_regress/t/t_langext_1d_bad.py | 18 ++++++++++++++++++ 4 files changed, 41 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_langext_1d.py create mode 100755 test_regress/t/t_langext_1d_bad.py diff --git a/Changes b/Changes index 8254e2147..7702f91a7 100644 --- a/Changes +++ b/Changes @@ -21,6 +21,7 @@ Verilator 5.033 devel * Fix pattern assignment to real inside struct (#5713). * Fix %p format output for real inside struct (#5713). * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] +* Fix matching language extension options including dots. Verilator 5.032 2025-01-01 diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 5e78fd40e..85bba8027 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -99,8 +99,10 @@ public: } void addLangExt(const string& langext, const V3LangCode& lc) { // New language extension replaces any pre-existing one. - (void)m_langExts.erase(langext); - m_langExts[langext] = lc; + string addext = langext; + if (addext[0] == '.') addext = addext.substr(1); + (void)m_langExts.erase(addext); + m_langExts[addext] = lc; } void addLibExtV(const string& libext) { diff --git a/test_regress/t/t_langext_1d.py b/test_regress/t/t_langext_1d.py new file mode 100755 index 000000000..6b8d4bc93 --- /dev/null +++ b/test_regress/t/t_langext_1d.py @@ -0,0 +1,18 @@ +#!/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('simulator') +test.top_filename = "t/t_langext_1.v" + +# This is a compile only test. +test.compile(v_flags2=["+verilog2001ext+.v"]) + +test.passes() diff --git a/test_regress/t/t_langext_1d_bad.py b/test_regress/t/t_langext_1d_bad.py new file mode 100755 index 000000000..706e9ab40 --- /dev/null +++ b/test_regress/t/t_langext_1d_bad.py @@ -0,0 +1,18 @@ +#!/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.top_filename = "t/t_langext_1.v" + +# This is a lint only test. +test.lint(v_flags2=["+verilog1995ext+.v"], fails=True) + +test.passes() From 0d34f56b7484510b709e5f70b184c70412126190 Mon Sep 17 00:00:00 2001 From: Andrew Nolte Date: Mon, 6 Jan 2025 15:21:32 -0500 Subject: [PATCH 17/93] Fix packages with `--public-depth 1` (#5708) --- src/V3EmitCSyms.cpp | 3 ++- src/V3LinkParse.cpp | 24 +++++++++++------------- src/V3Options.h | 3 ++- test_regress/t/t_vpi_package.cpp | 4 +++- test_regress/t/t_vpi_package.py | 2 +- test_regress/t/t_vpi_package.v | 19 ++++++++++++------- 6 files changed, 31 insertions(+), 24 deletions(-) diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 926342257..47b890ae8 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -983,7 +983,8 @@ void EmitCSyms::emitSymImp() { varName += protect(varp->name()); if (varp->isParam()) { - if (varp->vlEnumType() == "VLVT_STRING") { + if (varp->vlEnumType() == "VLVT_STRING" + && !VN_IS(varp->subDTypep(), UnpackArrayDType)) { puts(", const_cast(static_cast("); puts(varName); puts(".c_str())), "); diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index ebd9c98f1..4b2116ed0 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -340,22 +340,20 @@ class LinkParseVisitor final : public VNVisitor { // Maybe this variable has a signal attribute V3Config::applyVarAttr(m_modp, m_ftaskp, nodep); - if (v3Global.opt.publicFlatRW() - || (v3Global.opt.publicDepth() && m_modp - && (m_modp->level() - 1) <= v3Global.opt.publicDepth())) { - - switch (nodep->varType()) { - case VVarType::VAR: // FALLTHRU - case VVarType::GPARAM: // FALLTHRU - case VVarType::LPARAM: // FALLTHRU - case VVarType::PORT: // FALLTHRU - case VVarType::WIRE: nodep->sigUserRWPublic(true); break; - default: break; + if (v3Global.opt.anyPublicFlat() && nodep->varType().isVPIAccessible()) { + if (v3Global.opt.publicFlatRW()) { + nodep->sigUserRWPublic(true); + } else if (v3Global.opt.publicParams() && nodep->isParam()) { + nodep->sigUserRWPublic(true); + } else if (m_modp && v3Global.opt.publicDepth()) { + if ((m_modp->level() - 1) <= v3Global.opt.publicDepth()) { + nodep->sigUserRWPublic(true); + } else if (VN_IS(m_modp, Package) && nodep->isParam()) { + nodep->sigUserRWPublic(true); + } } } - if (v3Global.opt.publicParams() && nodep->isParam()) nodep->sigUserRWPublic(true); - // We used modTrace before leveling, and we may now // want to turn it off now that we know the levelizations if (v3Global.opt.traceDepth() && m_modp diff --git a/src/V3Options.h b/src/V3Options.h index eb3d14194..c1ed66ec9 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -543,6 +543,8 @@ public: bool allPublic() const { return m_public; } bool publicParams() const { return m_public_params; } bool publicFlatRW() const { return m_publicFlatRW; } + int publicDepth() const { return m_publicDepth; } + bool anyPublicFlat() const { return m_public_params || m_publicFlatRW || m_publicDepth; } bool lintOnly() const VL_MT_SAFE { return m_lintOnly; } bool ignc() const { return m_ignc; } bool quietExit() const VL_MT_SAFE { return m_quietExit; } @@ -577,7 +579,6 @@ public: int outputSplitCTrace() const { return m_outputSplitCTrace; } int outputGroups() const { return m_outputGroups; } int pinsBv() const VL_MT_SAFE { return m_pinsBv; } - int publicDepth() const { return m_publicDepth; } int reloopLimit() const { return m_reloopLimit; } VOptionBool skipIdentical() const { return m_skipIdentical; } bool stopFail() const { return m_stopFail; } diff --git a/test_regress/t/t_vpi_package.cpp b/test_regress/t/t_vpi_package.cpp index 4feb00a48..c2ba1eb52 100644 --- a/test_regress/t/t_vpi_package.cpp +++ b/test_regress/t/t_vpi_package.cpp @@ -116,10 +116,12 @@ int mon_check() { CHECK_RESULT_Z(count_params(unitHandle, 1)); CHECK_RESULT_Z(count_params(pkgHandle, 2)); - CHECK_RESULT_Z(count_params(tHandle, 3)); + CHECK_RESULT_Z(count_params(tHandle, 6)); CHECK_RESULT_Z(check_handle(const_cast("someOtherInt"), tHandle)) CHECK_RESULT_Z(check_handle(const_cast("t.someOtherInt"), NULL)) + CHECK_RESULT_Z(check_handle(const_cast("someString"), tHandle)) + CHECK_RESULT_Z(check_handle(const_cast("t.someString"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("someInt"), pkgHandle)) CHECK_RESULT_Z(check_handle(const_cast("somepackage::someInt"), NULL)) CHECK_RESULT_Z(check_handle(const_cast("dollarUnitInt"), unitHandle)) diff --git a/test_regress/t/t_vpi_package.py b/test_regress/t/t_vpi_package.py index 61cf61c9e..91593a3fe 100755 --- a/test_regress/t/t_vpi_package.py +++ b/test_regress/t/t_vpi_package.py @@ -14,7 +14,7 @@ test.scenarios('simulator') test.compile(make_top_shell=False, make_main=False, make_pli=True, - verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename]) + verilator_flags2=["--exe --vpi --no-l2name --public-depth 1", test.pli_filename]) test.execute(use_libvpi=True) diff --git a/test_regress/t/t_vpi_package.v b/test_regress/t/t_vpi_package.v index 9f9ca4161..b69e2cdad 100644 --- a/test_regress/t/t_vpi_package.v +++ b/test_regress/t/t_vpi_package.v @@ -8,19 +8,24 @@ import "DPI-C" context function int mon_check(); -parameter int dollarUnitInt /*verilator public_flat_rd*/ = 3; +parameter int dollarUnitInt = 3; package somepackage; - parameter int someInt /*verilator public_flat_rd*/ = 5; - parameter int anotherInt /*verilator public_flat_rd*/ = 6; + parameter int someInt = 5; + parameter int anotherInt = 6; endpackage module t (/*AUTOARG*/ - ); /*verilator public_module*/ + ); - parameter int someOtherInt /* verilator public_flat_rd*/ = 7; - parameter int yetAnotherInt /* verilator public_flat_rd*/ = 9; - parameter int stillAnotherInt /* verilator public_flat_rd*/ = 17; + parameter int someOtherInt = 7; + parameter int yetAnotherInt = 9; + parameter int stillAnotherInt = 17; + parameter int register = 0; + parameter int n_str = 2; + // Edge case with pvi code generation + parameter string someString [n_str] = '{default: ""}; + logic reference; integer status; From b8288b97f73a91e975959e854400b7d2edd1333b Mon Sep 17 00:00:00 2001 From: Andrew Nolte Date: Mon, 6 Jan 2025 17:56:18 -0500 Subject: [PATCH 18/93] Improve VPI write errors (#5712) --- include/verilated_vpi.cpp | 26 +++++++++++++------------- test_regress/t/t_vpi_const_type.cpp | 23 +++++++++++++++++++++-- test_regress/t/t_vpi_const_type.v | 3 ++- test_regress/t/t_vpi_var.cpp | 5 ++++- test_regress/t/t_vpi_var.v | 2 +- test_regress/t/t_vpi_var2.v | 2 +- 6 files changed, 42 insertions(+), 19 deletions(-) diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 6d3a3eaf7..795f367ae 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -2582,6 +2582,19 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ } PLI_INT32 delay_mode = flags & 0xfff; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { + VL_DEBUG_IF_PLI( + VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), + valuep->format, valuep->value.integer); + VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); + + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "vpi_put_value was used on signal marked read-only," + " use public_flat_rw instead for %s : %s", + vop->fullname(), vop->scopep()->defname()); + return nullptr; + } + if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr; if (delay_mode == vpiInertialDelay) { if (!VerilatedVpiPutHolder::canInertialDelay(valuep)) { VL_VPI_WARNING_( @@ -2593,20 +2606,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VerilatedVpiImp::inertialDelay(vop, valuep); return object; } - VL_DEBUG_IF_PLI( - VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), - valuep->format, valuep->value.integer); - VL_DBG_MSGF("- vpi: varp=%p putatp=%p\n", vop->varp()->datap(), vop->varDatap());); VerilatedVpiImp::evalNeeded(true); - - if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "Ignoring vpi_put_value to signal marked read-only," - " use public_flat_rw instead: %s", - vop->fullname()); - return nullptr; - } - if (!vl_check_format(vop->varp(), valuep, vop->fullname(), false)) return nullptr; if (valuep->format == vpiVectorVal) { if (VL_UNLIKELY(!valuep->value.vector)) return nullptr; if (vop->varp()->vltype() == VLVT_UINT8) { diff --git a/test_regress/t/t_vpi_const_type.cpp b/test_regress/t/t_vpi_const_type.cpp index ac8ebd2db..5efd97175 100644 --- a/test_regress/t/t_vpi_const_type.cpp +++ b/test_regress/t/t_vpi_const_type.cpp @@ -62,13 +62,32 @@ int mon_check() { const char* strConstTypeStr = vpi_get_str(vpiConstType, strHandle); CHECK_RESULT_CSTR(strConstTypeStr, "vpiStringConst") - TestVpiHandle sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal", NULL); + // t.signal_rd is not constant, and should error on a write + TestVpiHandle sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal_rd", NULL); CHECK_RESULT_NZ(sigHandle) PLI_INT32 sigConstType = vpi_get(vpiConstType, sigHandle); - // t.signal is not constant CHECK_RESULT(sigConstType, vpiUndefined) const char* sigConstTypeStr = vpi_get_str(vpiConstType, sigHandle); CHECK_RESULT_CSTR(sigConstTypeStr, "*undefined*") + // and should error on a write + s_vpi_value vpi_value; + vpi_value.format = vpiIntVal; + vpi_value.value.integer = 1; + vpi_put_value(sigHandle, &vpi_value, NULL, vpiNoDelay); + CHECK_RESULT(vpi_chk_error(nullptr), vpiError); + // and an intertial write + vpi_put_value(sigHandle, &vpi_value, NULL, vpiInertialDelay); + CHECK_RESULT(vpi_chk_error(nullptr), vpiError); + + // t.signal_rw is not constant + sigHandle = vpi_handle_by_name((PLI_BYTE8*)"t.signal_rw", NULL); + CHECK_RESULT_NZ(sigHandle) + sigConstType = vpi_get(vpiConstType, sigHandle); + CHECK_RESULT(sigConstType, vpiUndefined) + sigConstTypeStr = vpi_get_str(vpiConstType, sigHandle); + CHECK_RESULT_CSTR(sigConstTypeStr, "*undefined*") + + // left range of t.signal_rw TestVpiHandle leftHandle = vpi_handle(vpiLeftRange, sigHandle); CHECK_RESULT_NZ(leftHandle) PLI_INT32 leftConstType = vpi_get(vpiConstType, leftHandle); diff --git a/test_regress/t/t_vpi_const_type.v b/test_regress/t/t_vpi_const_type.v index ef76c8f35..2ae4e2f59 100644 --- a/test_regress/t/t_vpi_const_type.v +++ b/test_regress/t/t_vpi_const_type.v @@ -16,7 +16,8 @@ module t (/*AUTOARG*/ parameter time timeParam /*verilator public_flat_rd*/ = 0; parameter string strParam /*verilator public_flat_rd*/ = "abc"; - logic [31:0] signal /*verilator public_flat_rw*/; + logic [31:0] signal_rw /*verilator public_flat_rw*/; + logic [31:0] signal_rd /*verilator public_flat_rd*/; int status; diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index db721c306..99ab60f9f 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -702,6 +702,7 @@ int _mon_check_delayed() { CHECK_RESULT_Z(vpi_chk_error(nullptr)); // test unsupported vpiInertialDelay cases + // - should these also throw vpi errors? v.format = vpiStringVal; v.value.str = nullptr; vpi_put_value(vh, &v, &t, vpiInertialDelay); @@ -712,9 +713,11 @@ int _mon_check_delayed() { vpi_put_value(vh, &v, &t, vpiInertialDelay); CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + // This format throws an error now + Verilated::fatalOnVpiError(false); v.format = vpiObjTypeVal; vpi_put_value(vh, &v, &t, vpiInertialDelay); - CHECK_RESULT_NZ(vpi_chk_error(nullptr)); + Verilated::fatalOnVpiError(true); return 0; } diff --git a/test_regress/t/t_vpi_var.v b/test_regress/t/t_vpi_var.v index d93196cc9..240694b86 100644 --- a/test_regress/t/t_vpi_var.v +++ b/test_regress/t/t_vpi_var.v @@ -39,7 +39,7 @@ extern "C" int mon_check(); reg [0:61] quads[2:3] /*verilator public_flat_rw @(posedge clk) */; // verilator lint_on ASCRANGE - reg [31:0] count /*verilator public_flat_rd */; + reg [31:0] count /*verilator public_flat */; reg [31:0] half_count /*verilator public_flat_rd */; reg [31:0] delayed /*verilator public_flat_rw */; reg [31:0] delayed_mem [16] /*verilator public_flat_rw */; diff --git a/test_regress/t/t_vpi_var2.v b/test_regress/t/t_vpi_var2.v index 0c0019ddf..93b720f3d 100644 --- a/test_regress/t/t_vpi_var2.v +++ b/test_regress/t/t_vpi_var2.v @@ -51,7 +51,7 @@ extern "C" int mon_check(); reg invisible1; // verilator lint_on ASCRANGE -/*verilator public_flat_rd_on*/ +/*verilator public_flat_on*/ reg [31:0] count; reg [31:0] half_count; /*verilator public_off*/ From 481adf8fe79f73b46859febece64434af90bdf22 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 6 Jan 2025 17:56:50 -0500 Subject: [PATCH 19/93] Commentary --- docs/guide/warnings.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index bd8fb5b40..6197f8420 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -44,7 +44,7 @@ Warnings may be disabled in multiple ways: .. code-block:: sv - lint_off -rule UNSIGNED -file "*/example.v" -line 1 + lint_off -rule UNSIGNED -file "*/example.v" -lines 1 Error And Warning Format @@ -2009,12 +2009,12 @@ List Of Warnings .. code-block:: sv - wire _unused_ok = &{1'b0, + wire _unused_ok = 1'b0 && &{1'b0, sig_not_used_a, sig_not_used_yet_b, // To be fixed 1'b0}; - The reduction AND and constant zeros mean the net will always be zero, + The AND with constant zero mean the net will always be zero, so won't use simulation runtime. The redundant leading and trailing zeros avoid syntax errors if there are no signals between them. The magic name "unused" (controlled by the :vlopt:`--unused-regexp` option) From e171463fa2072fd0f8556a8f27fd8a2ae46abbce Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 6 Jan 2025 18:48:32 -0500 Subject: [PATCH 20/93] Add COVERIGN warning, as a more specific UNSUPPORTED error. --- Changes | 1 + docs/guide/warnings.rst | 12 + src/V3Error.h | 3 +- src/V3FileLine.h | 1 + src/V3Options.cpp | 6 + src/verilog.y | 97 ++++---- test_regress/t/t_covergroup_unsup.out | 261 +++++++++++----------- test_regress/t/t_covergroup_unsup.v | 4 +- test_regress/t/t_covergroup_unsup_ign.py | 17 ++ test_regress/t/t_sequence_sexpr_unsup.out | 7 +- 10 files changed, 225 insertions(+), 184 deletions(-) create mode 100755 test_regress/t/t_covergroup_unsup_ign.py diff --git a/Changes b/Changes index 7702f91a7..6e2f283fb 100644 --- a/Changes +++ b/Changes @@ -13,6 +13,7 @@ Verilator 5.033 devel **Minor:** +* Add COVERIGN warning, as a more specific UNSUPPORTED error. * Support generated classes (#5665). [Shou-Li Hsu] * Support `+incdir` with multiple directories. * Fix error message when call task as a function (#3089). [Matthew Ballance] diff --git a/docs/guide/warnings.rst b/docs/guide/warnings.rst index 6197f8420..387530491 100644 --- a/docs/guide/warnings.rst +++ b/docs/guide/warnings.rst @@ -445,6 +445,18 @@ List Of Warnings correctly. +.. option:: COVERIGN + + Warns that Verilator does not support certain forms of + :code:`covergroup`, :code:`coverpoint`, and coverage options, and the + construct was are ignored. + + Disabling the :option:`UNSUPPORTED` error also disables this warning. + + Ignoring this warning may make Verilator ignore lint checking on the + construct, and collect coverage data differently from other simulators. + + .. option:: DECLFILENAME .. TODO better example diff --git a/src/V3Error.h b/src/V3Error.h index c381ddfe7..dd69654f6 100644 --- a/src/V3Error.h +++ b/src/V3Error.h @@ -88,6 +88,7 @@ public: COMBDLY, // Combinatorial delayed assignment CONSTRAINTIGN, // Constraint ignored CONTASSREG, // Continuous assignment on reg + COVERIGN, // Coverage ignored DECLFILENAME, // Declaration doesn't match filename DEFPARAM, // Style: Defparam DEPRECATED, // Feature will be deprecated @@ -194,7 +195,7 @@ public: "ALWCOMBORDER", "ASCRANGE", "ASSIGNDLY", "ASSIGNIN", "BADSTDPRAGMA", "BLKANDNBLK", "BLKLOOPINIT", "BLKSEQ", "BSSPACE", "CASEINCOMPLETE", "CASEOVERLAP", "CASEWITHX", "CASEX", "CASTCONST", "CDCRSTLOGIC", "CLKDATA", - "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", + "CMPCONST", "COLONPLUS", "COMBDLY", "CONSTRAINTIGN", "CONTASSREG", "COVERIGN", "DECLFILENAME", "DEFPARAM", "DEPRECATED", "ENCAPSULATED", "ENDLABEL", "ENUMVALUE", "EOFNEWLINE", "GENCLK", "GENUNNAMED", "HIERBLOCK", diff --git a/src/V3FileLine.h b/src/V3FileLine.h index 5b934416a..e885cc7cd 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -275,6 +275,7 @@ public: warnOn(V3ErrorCode::WIDTHEXPAND, flag); warnOn(V3ErrorCode::WIDTHXZEXPAND, flag); } + if (code == V3ErrorCode::E_UNSUPPORTED) warnOn(V3ErrorCode::COVERIGN, flag); m_msgEnIdx = singleton().msgEnSetBit(m_msgEnIdx, code, flag); } void warnOff(V3ErrorCode code, bool flag) { warnOn(code, !flag); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 85bba8027..aad0856b9 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1744,6 +1744,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, V3Error::pretendError(V3ErrorCode::UNUSEDSIGNAL, false); V3Error::pretendError(V3ErrorCode::UNUSEDPARAM, false); }); + DECL_OPTION("-Wwarn-UNSUPPORTED", CbCall, []() { + FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, false); + FileLine::globalWarnOff(V3ErrorCode::COVERIGN, false); + V3Error::pretendError(V3ErrorCode::E_UNSUPPORTED, false); + V3Error::pretendError(V3ErrorCode::COVERIGN, false); + }); DECL_OPTION("-Wwarn-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, false); V3Error::pretendError(V3ErrorCode::WIDTH, false); diff --git a/src/verilog.y b/src/verilog.y index 08ac1a969..9e292785e 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -37,6 +37,7 @@ // Pick up new lexer #define yylex PARSEP->tokenToBison +#define BBCOVERIGN(fl, msg) (fl)->v3warn(COVERIGN, msg) #define BBUNSUP(fl, msg) (fl)->v3warn(E_UNSUPPORTED, msg) #define GATEUNSUP(fl, tok) \ { BBUNSUP((fl), "Unsupported: Verilog 1995 gate primitive: " << (tok)); } @@ -3947,16 +3948,16 @@ value_range: // ==IEEE: value_range/open_value_range covergroup_value_range: // ==IEEE-2012: covergroup_value_range cgexpr { $$ = $1; } | '[' cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); } // // IEEE-2023: added all four: // // Skipped as '$' is part of our expr // // IEEE-2023: '[' '$' ':' cgexpr ']' // // Skipped as '$' is part of our expr // // IEEE-2023: '[' cgexpr ':' '$' ']' | '[' cgexpr yP_PLUSSLASHMINUS cgexpr ']' - { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); } | '[' cgexpr yP_PLUSPCTMINUS cgexpr ']' - { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup value range"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup value range"); } ; caseCondList: // IEEE: part of case_item @@ -6173,13 +6174,13 @@ concurrent_assertion_statement: // ==IEEE: concurrent_assertion_statemen { $$ = new AstCover{$1, $4, $6, VAssertType::CONCURRENT}; } // // IEEE: cover_sequence_statement | yCOVER ySEQUENCE '(' sexpr ')' stmt - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); } // // IEEE: yCOVER ySEQUENCE '(' clocking_event sexpr ')' stmt // // sexpr already includes "clocking_event sexpr" | yCOVER ySEQUENCE '(' clocking_event yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); } | yCOVER ySEQUENCE '(' yDISABLE yIFF '(' expr/*expression_or_dist*/ ')' sexpr ')' stmt - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover sequence"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover sequence"); } // // IEEE: restrict_property_statement | yRESTRICT yPROPERTY '(' property_spec ')' ';' { $$ = new AstRestrict{$1, $4}; } @@ -6656,7 +6657,7 @@ covergroup_declaration: // ==IEEE: covergroup_declaration covergroup_declarationFront: // IEEE: part of covergroup_declaration yCOVERGROUP idAny { $$ = new AstConstraint{$2, *$2, nullptr}; - BBUNSUP($1, "Unsupported: covergroup"); + BBCOVERIGN($1, "Ignoring unsupported: covergroup"); SYMP->pushNew($$); } ; @@ -6686,27 +6687,27 @@ coverage_option: // ==IEEE: coverage_option // // option/type_option aren't really keywords id/*yOPTION | yTYPE_OPTION*/ '.' idAny/*member_identifier*/ '=' expr { // TODO: check that 'id' is 'option' or 'type_option' - $$ = nullptr; BBUNSUP($1, "Unsupported: coverage option"); } + $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage option"); } ; cover_point: // ==IEEE: cover_point // // [ [ data_type_or_implicit ] cover_point_identifier ':' ] yCOVERPOINT yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverpoint"); } // // IEEE-2012: class_scope before an ID | id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($3, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: coverpoint"); } // // data_type_or_implicit expansion | data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); } | yVAR data_type id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | yVAR implicit_typeE id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | signingE rangeList id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverpoint"); } | signing id/*cover_point_id*/ ':' yCOVERPOINT expr iffE bins_or_empty - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover point"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: coverpoint"); } // // IEEE-2012: | bins_or_empty { $$ = $1; } ; @@ -6714,7 +6715,7 @@ cover_point: // ==IEEE: cover_point iffE: // IEEE: part of cover_point, others /* empty */ { $$ = nullptr; } | yIFF '(' expr ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover 'iff'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover 'iff'"); } ; bins_or_empty: // ==IEEE: bins_or_empty @@ -6739,24 +6740,24 @@ bins_or_options: // ==IEEE: bins_or_options coverage_option { $$ = $1; } // // Can't use wildcardE as results in conflicts | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover bin specification"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin specification"); } | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE - { $$ = nullptr; BBUNSUP($8, "Unsupported: cover bin 'with' specification"); } + { $$ = nullptr; BBCOVERIGN($8, "Ignoring unsupported: cover bin 'with' specification"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'wildcard' specification"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'wildcard' specification"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__CUR '{' cgexpr '}' iffE - { $$ = nullptr; BBUNSUP($9, "Unsupported: cover bin 'wildcard' 'with' specification"); } + { $$ = nullptr; BBCOVERIGN($9, "Ignoring unsupported: cover bin 'wildcard' 'with' specification"); } // // // cgexpr part of trans_list | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE - { $$ = nullptr; BBUNSUP($4, "Unsupported: cover bin trans list"); } + { $$ = nullptr; BBCOVERIGN($4, "Ignoring unsupported: cover bin trans list"); } | yWILDCARD bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' trans_list iffE - { $$ = nullptr; BBUNSUP($1, "Unsupported: cover bin 'wildcard' trans list"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover bin 'wildcard' trans list"); } // | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT iffE - { $$ = nullptr; BBUNSUP($5, "Unsupported: cover bin 'default'"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: cover bin 'default'"); } | bins_keyword idAny/*bin_identifier*/ bins_orBraE '=' yDEFAULT ySEQUENCE iffE - { $$ = nullptr; BBUNSUP($6, "Unsupported: cover bin 'default' 'sequence'"); } + { $$ = nullptr; BBCOVERIGN($6, "Ignoring unsupported: cover bin 'default' 'sequence'"); } ; bins_orBraE: // IEEE: part of bins_or_options: @@ -6780,23 +6781,23 @@ trans_set: // ==IEEE: trans_set trans_range_list { $$ = $1; } // // Note the { => } in the grammar, this is really a list | trans_set yP_EQGT trans_range_list - { $$ = $1; BBUNSUP($2, "Unsupported: cover trans set '=>'"); } + { $$ = $1; BBCOVERIGN($2, "Ignoring unsupported: cover trans set '=>'"); } ; trans_range_list: // ==IEEE: trans_range_list trans_item { $$ = $1; } | trans_item yP_BRASTAR cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); } | trans_item yP_BRASTAR cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[*'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[*'"); } | trans_item yP_BRAMINUSGT cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); } | trans_item yP_BRAMINUSGT cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[->'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[->'"); } | trans_item yP_BRAEQ cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); } | trans_item yP_BRAEQ cgexpr ':' cgexpr ']' - { $$ = nullptr; BBUNSUP($2, "Unsupported: cover '[='"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: cover '[='"); } ; trans_item: // ==IEEE: range_list @@ -6811,9 +6812,9 @@ covergroup_range_list: // ==IEEE: covergroup_range_list cover_cross: // ==IEEE: cover_cross id/*cover_point_identifier*/ ':' yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBUNSUP($3, "Unsupported: cross"); } + { $$ = nullptr; BBCOVERIGN($3, "Ignoring unsupported: cover cross"); } | yCROSS list_of_cross_items iffE cross_body - { $$ = nullptr; BBUNSUP($1, "Unsupported: cross"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: cover cross"); } ; list_of_cross_items: // ==IEEE: list_of_cross_items @@ -6855,25 +6856,25 @@ cross_body_item: // ==IEEE: cross_body_item coverage_option { $$ = $1; } // // IEEE: bins_selection | function_declaration - { $$ = $1; BBUNSUP($1->fileline(), "Unsupported: coverage cross 'function' declaration"); } + { $$ = $1; BBCOVERIGN($1->fileline(), "Ignoring unsupported: coverage cross 'function' declaration"); } | bins_keyword idAny/*new-bin_identifier*/ '=' select_expression iffE - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage cross bin"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage cross bin"); } ; select_expression: // ==IEEE: select_expression // // IEEE: select_condition expanded here yBINSOF '(' bins_expression ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); } | '!' yBINSOF '(' bins_expression ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression 'binsof'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression 'binsof'"); } | yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' - { $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); } | '!' yBINSOF '(' bins_expression ')' yINTERSECT '{' covergroup_range_list '}' { } - { $$ = nullptr; BBUNSUP($5, "Unsupported: coverage select expression 'intersect'"); } + { $$ = nullptr; BBCOVERIGN($5, "Ignoring unsupported: coverage select expression 'intersect'"); } | yWITH__PAREN '(' cgexpr ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression with"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); } | '!' yWITH__PAREN '(' cgexpr ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression with"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression with"); } // // IEEE-2012: Need clarification as to precedence //UNSUP yWITH__PAREN '(' cgexpr ')' yMATCHES cgexpr { } // // IEEE-2012: Need clarification as to precedence @@ -6881,13 +6882,13 @@ select_expression: // ==IEEE: select_expression // | '(' select_expression ')' { $$ = $2; } | select_expression yP_ANDAND select_expression - { $$ = nullptr; BBUNSUP($2, "Unsupported: coverage select expression '&&'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '&&'"); } | select_expression yP_OROR select_expression - { $$ = nullptr; BBUNSUP($2, "Unsupported: coverage select expression '||'"); } + { $$ = nullptr; BBCOVERIGN($2, "Ignoring unsupported: coverage select expression '||'"); } // // IEEE-2012: cross_identifier // // Part of covergroup_expression - generic identifier // // IEEE-2012: Need clarification as to precedence - //UNSUP cgexpr { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage select expression"); } + //UNSUP cgexpr { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage select expression"); } // // // Need precedence fix //UNSUP cgexpr yMATCHES cgexpr {..} @@ -6904,11 +6905,11 @@ bins_expression: // ==IEEE: bins_expression coverage_eventE: // IEEE: [ coverage_event ] /* empty */ { $$ = nullptr; } | clocking_event - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage clocking event"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage clocking event"); } | yWITH__ETC yFUNCTION idAny/*"sample"*/ '(' tf_port_listE ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage 'with' 'function'"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage 'with' 'function'"); } | yP_ATAT '(' block_event_expression ')' - { $$ = nullptr; BBUNSUP($1, "Unsupported: coverage '@@' events"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: coverage '@@' events"); } ; block_event_expression: // ==IEEE: block_event_expression @@ -7352,7 +7353,7 @@ class_item: // ==IEEE: class_item { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } | timeunits_declaration { $$ = $1; } | covergroup_declaration - { $$ = nullptr; BBUNSUP($1, "Unsupported: covergroup within class"); } + { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); } // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } | ';' { $$ = nullptr; } diff --git a/test_regress/t/t_covergroup_unsup.out b/test_regress/t/t_covergroup_unsup.out index 808facb6d..8c27e0ad6 100644 --- a/test_regress/t/t_covergroup_unsup.out +++ b/test_regress/t/t_covergroup_unsup.out @@ -1,383 +1,384 @@ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:25:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:25:4: Ignoring unsupported: covergroup 25 | covergroup cg_empty; | ^~~~~~~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:28:4: Unsupported: covergroup + ... For warning description see https://verilator.org/warn/COVERIGN?v=latest + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_covergroup_unsup.v:28:4: Ignoring unsupported: covergroup 28 | covergroup cg_opt; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:29:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:29:7: Ignoring unsupported: coverage option 29 | type_option.weight = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:30:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:30:7: Ignoring unsupported: coverage option 30 | type_option.goal = 99; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:31:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:31:7: Ignoring unsupported: coverage option 31 | type_option.comment = "type_option_comment"; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:32:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:32:7: Ignoring unsupported: coverage option 32 | type_option.strobe = 0; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:33:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:33:7: Ignoring unsupported: coverage option 33 | type_option.merge_instances = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:34:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:34:7: Ignoring unsupported: coverage option 34 | type_option.distribuge_first = 1; | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:35:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:35:7: Ignoring unsupported: coverage option 35 | option.name = "the_name"; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:36:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:36:7: Ignoring unsupported: coverage option 36 | option.weight = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:37:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:37:7: Ignoring unsupported: coverage option 37 | option.goal = 98; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:38:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:38:7: Ignoring unsupported: coverage option 38 | option.comment = "option_comment"; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:39:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:39:7: Ignoring unsupported: coverage option 39 | option.at_least = 20; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:40:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:40:7: Ignoring unsupported: coverage option 40 | option.auto_bin_max = 10; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:41:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:41:7: Ignoring unsupported: coverage option 41 | option.cross_num_print_missing = 2; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:42:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:42:7: Ignoring unsupported: coverage option 42 | option.detect_overlap = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:43:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:43:7: Ignoring unsupported: coverage option 43 | option.per_instance = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:44:7: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:44:7: Ignoring unsupported: coverage option 44 | option.get_inst_coverage = 1; | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:47:4: Ignoring unsupported: covergroup 47 | covergroup cg_clockingevent() @(posedge clk); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:47:34: Unsupported: coverage clocking event +%Warning-COVERIGN: t/t_covergroup_unsup.v:47:34: Ignoring unsupported: coverage clocking event 47 | covergroup cg_clockingevent() @(posedge clk); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:49:4: Ignoring unsupported: covergroup 49 | covergroup cg_withfunction() with function sample (a); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:49:33: Unsupported: coverage 'with' 'function' +%Warning-COVERIGN: t/t_covergroup_unsup.v:49:33: Ignoring unsupported: coverage 'with' 'function' 49 | covergroup cg_withfunction() with function sample (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:51:4: Ignoring unsupported: covergroup 51 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:51:25: Unsupported: coverage '@@' events +%Warning-COVERIGN: t/t_covergroup_unsup.v:51:25: Ignoring unsupported: coverage '@@' events 51 | covergroup cg_atat() @@ (begin funca or end funcb); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:53:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:53:4: Ignoring unsupported: covergroup 53 | covergroup cg_bracket; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:56:4: Unsupported: covergroup - 56 | covergroup cg_bracket; +%Warning-COVERIGN: t/t_covergroup_unsup.v:56:4: Ignoring unsupported: covergroup + 56 | covergroup cg_bracket2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:57:9: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:57:9: Ignoring unsupported: coverage option 57 | { option.name = "option"; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:59:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:59:4: Ignoring unsupported: covergroup 59 | covergroup cg_cp; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:60:7: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:60:7: Ignoring unsupported: coverpoint 60 | coverpoint a; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:62:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:62:4: Ignoring unsupported: covergroup 62 | covergroup cg_cp_iff; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:20: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:63:20: Ignoring unsupported: cover 'iff' 63 | coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:63:7: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:63:7: Ignoring unsupported: coverpoint 63 | coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:65:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:65:4: Ignoring unsupported: covergroup 65 | covergroup cg_id_cp_iff; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:24: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:66:24: Ignoring unsupported: cover 'iff' 66 | id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:66:11: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:66:11: Ignoring unsupported: coverpoint 66 | id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:68:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:68:4: Ignoring unsupported: covergroup 68 | covergroup cg_id_cp_id1; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:28: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:69:28: Ignoring unsupported: cover 'iff' 69 | int id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:69:15: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:69:15: Ignoring unsupported: coverpoint 69 | int id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:71:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:71:4: Ignoring unsupported: covergroup 71 | covergroup cg_id_cp_id2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:32: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:72:32: Ignoring unsupported: cover 'iff' 72 | var int id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:72:19: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:72:19: Ignoring unsupported: coverpoint 72 | var int id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:74:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:74:4: Ignoring unsupported: covergroup 74 | covergroup cg_id_cp_id3; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:34: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:75:34: Ignoring unsupported: cover 'iff' 75 | var [3:0] id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:75:21: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:75:21: Ignoring unsupported: coverpoint 75 | var [3:0] id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:77:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:77:4: Ignoring unsupported: covergroup 77 | covergroup cg_id_cp_id4; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:30: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:78:30: Ignoring unsupported: cover 'iff' 78 | [3:0] id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:78:17: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:78:17: Ignoring unsupported: coverpoint 78 | [3:0] id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:80:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:80:4: Ignoring unsupported: covergroup 80 | covergroup cg_id_cp_id5; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:31: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:81:31: Ignoring unsupported: cover 'iff' 81 | signed id: coverpoint a iff (b); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:81:18: Unsupported: cover point +%Warning-COVERIGN: t/t_covergroup_unsup.v:81:18: Ignoring unsupported: coverpoint 81 | signed id: coverpoint a iff (b); | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:84:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:84:4: Ignoring unsupported: covergroup 84 | covergroup cg_cross; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:18: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:85:18: Ignoring unsupported: cover 'iff' 85 | cross a, b iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:85:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:85:7: Ignoring unsupported: cover cross 85 | cross a, b iff (!rst); | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:87:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:87:4: Ignoring unsupported: covergroup 87 | covergroup cg_cross2; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:18: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:88:18: Ignoring unsupported: cover 'iff' 88 | cross a, b iff (!rst) {} | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:88:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:88:7: Ignoring unsupported: cover cross 88 | cross a, b iff (!rst) {} | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:90:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:90:4: Ignoring unsupported: covergroup 90 | covergroup cg_cross3; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:20: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:20: Ignoring unsupported: coverage option 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:46: Unsupported: coverage option +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:46: Ignoring unsupported: coverage option 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:91:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:91:7: Ignoring unsupported: cover cross 91 | cross a, b { option.comment = "cross"; option.weight = 12; } | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:93:4: Unsupported: covergroup - 93 | covergroup cg_cross3; +%Warning-COVERIGN: t/t_covergroup_unsup.v:93:4: Ignoring unsupported: covergroup + 93 | covergroup cg_cross4; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:34: Unsupported: coverage cross 'function' declaration +%Warning-COVERIGN: t/t_covergroup_unsup.v:94:34: Ignoring unsupported: coverage cross 'function' declaration 94 | cross a, b { function void crossfunc; endfunction; } | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:94:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:94:7: Ignoring unsupported: cover cross 94 | cross a, b { function void crossfunc; endfunction; } | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:96:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:96:4: Ignoring unsupported: covergroup 96 | covergroup cg_cross_id; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:28: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:97:28: Ignoring unsupported: cover 'iff' 97 | my_cg_id: cross a, b iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:97:17: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:97:17: Ignoring unsupported: cover cross 97 | my_cg_id: cross a, b iff (!rst); | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:100:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:100:4: Ignoring unsupported: covergroup 100 | covergroup cg_binsoroptions_bk1; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:102:17: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:102:17: Ignoring unsupported: cover bin specification 102 | { bins ba = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:24: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:103:24: Ignoring unsupported: cover 'iff' 103 | { bins bar = {a} iff (!rst); } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:103:18: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:103:18: Ignoring unsupported: cover bin specification 103 | { bins bar = {a} iff (!rst); } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:104:26: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:104:26: Ignoring unsupported: cover bin specification 104 | { illegal_bins ila = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:105:25: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:105:25: Ignoring unsupported: cover bin specification 105 | { ignore_bins iga = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:107:19: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:107:19: Ignoring unsupported: cover bin specification 107 | { bins ba[] = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:108:20: Unsupported: cover bin specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:108:20: Ignoring unsupported: cover bin specification 108 | { bins ba[2] = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:110:23: Unsupported: cover bin 'with' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:110:23: Ignoring unsupported: cover bin 'with' specification 110 | { bins ba = {a} with { b }; } | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:112:27: Unsupported: cover bin 'wildcard' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:112:27: Ignoring unsupported: cover bin 'wildcard' specification 112 | { wildcard bins bwa = {a}; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:113:34: Unsupported: cover bin 'wildcard' 'with' specification +%Warning-COVERIGN: t/t_covergroup_unsup.v:113:34: Ignoring unsupported: cover bin 'wildcard' 'with' specification 113 | { wildcard bins bwaw = {a} with { b }; } | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:115:20: Unsupported: cover bin 'default' +%Warning-COVERIGN: t/t_covergroup_unsup.v:115:20: Ignoring unsupported: cover bin 'default' 115 | { bins def = default; } | ^~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:116:29: Unsupported: cover bin 'default' 'sequence' +%Warning-COVERIGN: t/t_covergroup_unsup.v:116:29: Ignoring unsupported: cover bin 'default' 'sequence' 116 | { bins defs = default sequence; } | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:118:18: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:118:18: Ignoring unsupported: cover bin trans list 118 | { bins bts = ( 1, 2 ); } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:119:9: Unsupported: cover bin 'wildcard' trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:119:9: Ignoring unsupported: cover bin 'wildcard' trans list 119 | { wildcard bins wbts = ( 1, 2 ); } | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:120:33: Unsupported: covergroup value range +%Warning-COVERIGN: t/t_covergroup_unsup.v:120:33: Ignoring unsupported: covergroup value range 120 | { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:120:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:120:19: Ignoring unsupported: cover bin trans list 120 | { bins bts2 = ( 2, 3 ), ( [5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:122:27: Unsupported: cover trans set '=>' +%Warning-COVERIGN: t/t_covergroup_unsup.v:122:27: Ignoring unsupported: cover trans set '=>' 122 | { bins bts2 = ( 1,5 => 6,7 ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:122:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:122:19: Ignoring unsupported: cover bin trans list 122 | { bins bts2 = ( 1,5 => 6,7 ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:25: Unsupported: cover '[*' +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:25: Ignoring unsupported: cover '[*' 123 | { bins bts2 = ( 3 [*5] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:123:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:123:19: Ignoring unsupported: cover bin trans list 123 | { bins bts2 = ( 3 [*5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:25: Unsupported: cover '[*' +%Warning-COVERIGN: t/t_covergroup_unsup.v:124:25: Ignoring unsupported: cover '[*' 124 | { bins bts2 = ( 3 [*5:6] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:124:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:124:19: Ignoring unsupported: cover bin trans list 124 | { bins bts2 = ( 3 [*5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:25: Unsupported: cover '[->' +%Warning-COVERIGN: t/t_covergroup_unsup.v:125:25: Ignoring unsupported: cover '[->' 125 | { bins bts2 = ( 3 [->5] ) ; } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:125:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:125:19: Ignoring unsupported: cover bin trans list 125 | { bins bts2 = ( 3 [->5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:25: Unsupported: cover '[->' +%Warning-COVERIGN: t/t_covergroup_unsup.v:126:25: Ignoring unsupported: cover '[->' 126 | { bins bts2 = ( 3 [->5:6] ) ; } | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:126:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:126:19: Ignoring unsupported: cover bin trans list 126 | { bins bts2 = ( 3 [->5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:25: Unsupported: cover '[=' +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:25: Ignoring unsupported: cover '[=' 127 | { bins bts2 = ( 3 [=5] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:127:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:127:19: Ignoring unsupported: cover bin trans list 127 | { bins bts2 = ( 3 [=5] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:25: Unsupported: cover '[=' +%Warning-COVERIGN: t/t_covergroup_unsup.v:128:25: Ignoring unsupported: cover '[=' 128 | { bins bts2 = ( 3 [=5:6] ) ; } | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:128:19: Unsupported: cover bin trans list +%Warning-COVERIGN: t/t_covergroup_unsup.v:128:19: Ignoring unsupported: cover bin trans list 128 | { bins bts2 = ( 3 [=5:6] ) ; } | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:132:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:132:4: Ignoring unsupported: covergroup 132 | covergroup cg_cross_bins; | ^~~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:23: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:134:23: Ignoring unsupported: coverage select expression 'binsof' 134 | bins bin_a = binsof(a); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:134:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:134:10: Ignoring unsupported: coverage cross bin 134 | bins bin_a = binsof(a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:24: Ignoring unsupported: coverage select expression 'binsof' 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:34: Unsupported: cover 'iff' +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:34: Ignoring unsupported: cover 'iff' 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:135:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:135:10: Ignoring unsupported: coverage cross bin 135 | bins bin_ai = binsof(a) iff (!rst); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:136:23: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:23: Ignoring unsupported: coverage select expression 'binsof' 136 | bins bin_c = binsof(cp.x); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:136:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:136:10: Ignoring unsupported: coverage cross bin 136 | bins bin_c = binsof(cp.x); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:137:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:24: Ignoring unsupported: coverage select expression 'binsof' 137 | bins bin_na = ! binsof(a); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:137:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:137:10: Ignoring unsupported: coverage cross bin 137 | bins bin_na = ! binsof(a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:139:33: Unsupported: coverage select expression 'intersect' +%Warning-COVERIGN: t/t_covergroup_unsup.v:139:33: Ignoring unsupported: coverage select expression 'intersect' 139 | bins bin_d = binsof(a) intersect { b }; | ^~~~~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:139:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:139:10: Ignoring unsupported: coverage cross bin 139 | bins bin_d = binsof(a) intersect { b }; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:140:34: Unsupported: coverage select expression 'intersect' +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:34: Ignoring unsupported: coverage select expression 'intersect' 140 | bins bin_nd = ! binsof(a) intersect { b }; | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:140:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:140:10: Ignoring unsupported: coverage cross bin 140 | bins bin_nd = ! binsof(a) intersect { b }; | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:23: Unsupported: coverage select expression with +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:23: Ignoring unsupported: coverage select expression with 142 | bins bin_e = with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:142:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:142:10: Ignoring unsupported: coverage cross bin 142 | bins bin_e = with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:23: Unsupported: coverage select expression with +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:23: Ignoring unsupported: coverage select expression with 143 | bins bin_e = ! with (a); | ^ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:143:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:143:10: Ignoring unsupported: coverage cross bin 143 | bins bin_e = ! with (a); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:26: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:26: Ignoring unsupported: coverage select expression 'binsof' 145 | bins bin_par = (binsof(a)); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:145:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:145:10: Ignoring unsupported: coverage cross bin 145 | bins bin_par = (binsof(a)); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:25: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:25: Ignoring unsupported: coverage select expression 'binsof' 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:38: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:38: Ignoring unsupported: coverage select expression 'binsof' 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:35: Unsupported: coverage select expression '&&' +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:35: Ignoring unsupported: coverage select expression '&&' 146 | bins bin_and = binsof(a) && binsof(b); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:146:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:146:10: Ignoring unsupported: coverage cross bin 146 | bins bin_and = binsof(a) && binsof(b); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:24: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:24: Ignoring unsupported: coverage select expression 'binsof' 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:37: Unsupported: coverage select expression 'binsof' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:37: Ignoring unsupported: coverage select expression 'binsof' 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:34: Unsupported: coverage select expression '||' +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:34: Ignoring unsupported: coverage select expression '||' 147 | bins bin_or = binsof(a) || binsof(b); | ^~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:147:10: Unsupported: coverage cross bin +%Warning-COVERIGN: t/t_covergroup_unsup.v:147:10: Ignoring unsupported: coverage cross bin 147 | bins bin_or = binsof(a) || binsof(b); | ^~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:133:7: Unsupported: cross +%Warning-COVERIGN: t/t_covergroup_unsup.v:133:7: Ignoring unsupported: cover cross 133 | cross a, b { | ^~~~~ -%Error-UNSUPPORTED: t/t_covergroup_unsup.v:151:4: Unsupported: covergroup +%Warning-COVERIGN: t/t_covergroup_unsup.v:151:4: Ignoring unsupported: covergroup 151 | covergroup cg_more extends cg_empty; | ^~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_covergroup_unsup.v b/test_regress/t/t_covergroup_unsup.v index 1d8dcade6..04d78b154 100644 --- a/test_regress/t/t_covergroup_unsup.v +++ b/test_regress/t/t_covergroup_unsup.v @@ -53,7 +53,7 @@ module t (/*AUTOARG*/ covergroup cg_bracket; {} endgroup - covergroup cg_bracket; + covergroup cg_bracket2; { option.name = "option"; } endgroup covergroup cg_cp; @@ -90,7 +90,7 @@ module t (/*AUTOARG*/ covergroup cg_cross3; cross a, b { option.comment = "cross"; option.weight = 12; } endgroup - covergroup cg_cross3; + covergroup cg_cross4; cross a, b { function void crossfunc; endfunction; } endgroup covergroup cg_cross_id; diff --git a/test_regress/t/t_covergroup_unsup_ign.py b/test_regress/t/t_covergroup_unsup_ign.py new file mode 100755 index 000000000..47c0cd627 --- /dev/null +++ b/test_regress/t/t_covergroup_unsup_ign.py @@ -0,0 +1,17 @@ +#!/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('vlt') +test.top_filename = "t/t_covergroup_unsup.v" + +test.lint(verilator_flags2=['--assert --coverage --Wno-COVERIGN']) + +test.passes() diff --git a/test_regress/t/t_sequence_sexpr_unsup.out b/test_regress/t/t_sequence_sexpr_unsup.out index f72f26834..cdd372742 100644 --- a/test_regress/t/t_sequence_sexpr_unsup.out +++ b/test_regress/t/t_sequence_sexpr_unsup.out @@ -230,13 +230,14 @@ %Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:126:7: Unsupported: first_match (in sequence expression) 126 | first_match (a, res0 = 1, res1 = 2); | ^~~~~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:129:10: Unsupported: cover sequence +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:129:10: Ignoring unsupported: cover sequence 129 | cover sequence (s_a) $display(""); | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:130:10: Unsupported: cover sequence + ... Use "/* verilator lint_off COVERIGN */" and lint_on around source to disable this message. +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:130:10: Ignoring unsupported: cover sequence 130 | cover sequence (@(posedge a) disable iff (b) s_a) $display(""); | ^~~~~~~~ -%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:131:10: Unsupported: cover sequence +%Warning-COVERIGN: t/t_sequence_sexpr_unsup.v:131:10: Ignoring unsupported: cover sequence 131 | cover sequence (disable iff (b) s_a) $display(""); | ^~~~~~~~ %Error: Exiting due to From 87d856339f73131edcbb1c8d3cc2a51f6cbcc5b1 Mon Sep 17 00:00:00 2001 From: Nick Brereton <85175726+nbstrike@users.noreply.github.com> Date: Tue, 7 Jan 2025 16:52:44 -0500 Subject: [PATCH 21/93] Add lint error on importing package within a class (#5634) (#5679) --- docs/CONTRIBUTORS | 1 + src/V3LinkParse.cpp | 7 +++++++ test_regress/t/t_class_scope_import.out | 4 ++++ test_regress/t/t_class_scope_import.py | 16 ++++++++++++++++ test_regress/t/t_class_scope_import.v | 15 +++++++++++++++ test_regress/t/t_queue_unknown_sel.v | 25 ++++++++++++------------- 6 files changed, 55 insertions(+), 13 deletions(-) create mode 100644 test_regress/t/t_class_scope_import.out create mode 100755 test_regress/t/t_class_scope_import.py create mode 100644 test_regress/t/t_class_scope_import.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index fa06407a2..d6ad08718 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -159,6 +159,7 @@ Nandu Raj Nathan Graybeal Nathan Kohagen Nathan Myers +Nick Brereton Nolan Poe Oleh Maksymenko Patrick Stewart diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 4b2116ed0..782ea6c12 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -893,6 +893,13 @@ class LinkParseVisitor final : public VNVisitor { } iterateChildren(nodep); } + void visit(AstPackageImport* nodep) override { + cleanFileline(nodep); + if (m_modp && !m_ftaskp && VN_IS(m_modp, Class)) { + nodep->v3error("Import statement directly within a class scope is illegal"); + } + iterateChildren(nodep); + } void visit(AstNode* nodep) override { // Default: Just iterate diff --git a/test_regress/t/t_class_scope_import.out b/test_regress/t/t_class_scope_import.out new file mode 100644 index 000000000..4c1811e42 --- /dev/null +++ b/test_regress/t/t_class_scope_import.out @@ -0,0 +1,4 @@ +%Error: t/t_class_scope_import.v:11:14: Import statement directly within a class scope is illegal + 11 | import pkg::*; + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_class_scope_import.py b/test_regress/t/t_class_scope_import.py new file mode 100755 index 000000000..55203b6c9 --- /dev/null +++ b/test_regress/t/t_class_scope_import.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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() diff --git a/test_regress/t/t_class_scope_import.v b/test_regress/t/t_class_scope_import.v new file mode 100644 index 000000000..0306ffa6a --- /dev/null +++ b/test_regress/t/t_class_scope_import.v @@ -0,0 +1,15 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder +// SPDX-License-Identifier: CC0-1.0 + +package pkg; +endpackage + +class genericClass; + import pkg::*; +endclass + +module tb_top(); +endmodule diff --git a/test_regress/t/t_queue_unknown_sel.v b/test_regress/t/t_queue_unknown_sel.v index 489c7bcee..fa75b6c34 100644 --- a/test_regress/t/t_queue_unknown_sel.v +++ b/test_regress/t/t_queue_unknown_sel.v @@ -262,23 +262,22 @@ interface z_if; ); endinterface class z_txn_class; - import z_pkg::*; - rand txn_type_t req_txn_type; - rand cid_t cid; - rand sid_t sid; - rand ctag_t ctag; - rand stag_t stag; - rand size_t size; - rand address_t address; - rand state_t state1; - rand state_t state2; - rand state_t state3; + rand z_pkg::txn_type_t req_txn_type; + rand z_pkg::cid_t cid; + rand z_pkg::sid_t sid; + rand z_pkg::ctag_t ctag; + rand z_pkg::stag_t stag; + rand z_pkg::size_t size; + rand z_pkg::address_t address; + rand z_pkg::state_t state1; + rand z_pkg::state_t state2; + rand z_pkg::state_t state3; rand logic f1; rand logic f2; rand logic f3; rand logic f4; - data_t data[]; - mask_t mask[]; + z_pkg::data_t data[]; + z_pkg::mask_t mask[]; bit corrupt[]; logic [2:0] req_opcode; endclass From 260ac342f41682b1070747ef6b06826f5433f6e4 Mon Sep 17 00:00:00 2001 From: Krzysztof Starecki Date: Wed, 8 Jan 2025 15:43:57 +0100 Subject: [PATCH 22/93] Tests: Add support for Questa in TestSimulator class (#5718) --- test_regress/driver.py | 2 +- test_regress/t/TestSimulator.h | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/test_regress/driver.py b/test_regress/driver.py index 86ff9f340..65ff4fbe3 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -1418,7 +1418,7 @@ class VlTest: cmd = [ "echo q | " + run_env + VtOs.getenv_def('VERILATOR_MODELSIM', "vsim"), ' '.join(param['ms_run_flags']), ' '.join(param['all_run_flags']), pli_opt, - (" top") + (" t") ] self.run(cmd=cmd, check_finished=param['check_finished'], diff --git a/test_regress/t/TestSimulator.h b/test_regress/t/TestSimulator.h index b99537a78..22fc34cd4 100644 --- a/test_regress/t/TestSimulator.h +++ b/test_regress/t/TestSimulator.h @@ -17,11 +17,12 @@ class TestSimulator { private: struct SimTypes { - int verilator; - int icarus; - int mti; - int ncsim; - int vcs; + bool verilator = false; + bool icarus = false; + bool mti = false; + bool ncsim = false; + bool vcs = false; + bool questa = false; }; s_vpi_vlog_info m_info; SimTypes m_simulators; @@ -37,6 +38,8 @@ public: == strncmp(m_info.product, "Chronologic Simulation VCS", std::strlen("Chronologic Simulation VCS"))) { m_simulators.vcs = true; + } else if (0 == strcmp(m_info.product, "ModelSim for Questa-64")) { + m_simulators.questa = true; } else { printf("%%Warning: %s:%d: Unknown simulator in TestSimulator.h: %s\n", __FILE__, __LINE__, m_info.product); @@ -59,12 +62,13 @@ public: static bool is_mti() { return simulators().mti; } static bool is_ncsim() { return simulators().ncsim; } static bool is_vcs() { return simulators().vcs; } + static bool is_questa() { return simulators().questa; } // Simulator properties static bool is_event_driven() { return !simulators().verilator; } static bool has_get_scalar() { return !simulators().icarus; } // return test level scope static const char* top() { - if (simulators().verilator || simulators().icarus) { + if (simulators().verilator || simulators().icarus || simulators().questa) { return "t"; } else { return "top.t"; From ff244c1d1f0c33ed8fedf905265872582c693d72 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 8 Jan 2025 14:44:48 +0000 Subject: [PATCH 23/93] Apply 'make format' --- test_regress/driver.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/test_regress/driver.py b/test_regress/driver.py index 65ff4fbe3..f9864103e 100755 --- a/test_regress/driver.py +++ b/test_regress/driver.py @@ -1417,8 +1417,7 @@ class VlTest: pli_opt = "-pli " + self.obj_dir + "/libvpi.so" cmd = [ "echo q | " + run_env + VtOs.getenv_def('VERILATOR_MODELSIM', "vsim"), - ' '.join(param['ms_run_flags']), ' '.join(param['all_run_flags']), pli_opt, - (" t") + ' '.join(param['ms_run_flags']), ' '.join(param['all_run_flags']), pli_opt, (" t") ] self.run(cmd=cmd, check_finished=param['check_finished'], From 44f49669a3a76fddea7c7cbe3b43b2ff471cb235 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 8 Jan 2025 19:37:20 -0500 Subject: [PATCH 24/93] Support side effects of form `variable[index_function()]++`. --- Changes | 1 + src/V3Ast.cpp | 18 ++++--- src/V3Ast.h | 1 + src/V3LinkInc.cpp | 70 ++++++++++++++++++++++++++-- src/verilog.y | 12 +++-- test_regress/t/t_array_index_side.py | 18 +++++++ test_regress/t/t_array_index_side.v | 54 +++++++++++++++++++++ test_regress/t/t_stmt_incr_unsup.out | 14 ++++-- test_regress/t/t_stmt_incr_unsup.v | 4 +- 9 files changed, 172 insertions(+), 20 deletions(-) create mode 100755 test_regress/t/t_array_index_side.py create mode 100644 test_regress/t/t_array_index_side.v diff --git a/Changes b/Changes index 6e2f283fb..1ab276672 100644 --- a/Changes +++ b/Changes @@ -16,6 +16,7 @@ Verilator 5.033 devel * Add COVERIGN warning, as a more specific UNSUPPORTED error. * Support generated classes (#5665). [Shou-Li Hsu] * Support `+incdir` with multiple directories. +* Support side effects of form 'variable[index_function()]++'. * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index c7d926a3e..cf1873b6a 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -797,13 +797,7 @@ void AstNode::swapWith(AstNode* bp) { AstNode* AstNode::cloneTreeIter(bool needPure) { // private: Clone single node and children - if (VL_UNLIKELY(needPure && !isPure())) { - this->v3warn(SIDEEFFECT, - "Expression side effect may be mishandled\n" - << this->warnMore() - << "... Suggest use a temporary variable in place of this expression"); - // this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused - } + if (needPure) purityCheck(); AstNode* const newp = this->clone(); if (this->m_op1p) newp->op1p(this->m_op1p->cloneTreeIterList(needPure)); if (this->m_op2p) newp->op2p(this->m_op2p->cloneTreeIterList(needPure)); @@ -850,6 +844,16 @@ AstNode* AstNode::cloneTree(bool cloneNextLink, bool needPure) { return newp; } +void AstNode::purityCheck() { + if (VL_UNLIKELY(!isPure())) { + this->v3warn(SIDEEFFECT, + "Expression side effect may be mishandled\n" + << this->warnMore() + << "... Suggest use a temporary variable in place of this expression"); + // this->v3fatalSrc("cloneTreePure debug backtrace"); // Comment in to debug where caused + } +} + //====================================================================== // Delete diff --git a/src/V3Ast.h b/src/V3Ast.h index 7d6ddf3dc..ba483fa94 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -2077,6 +2077,7 @@ private: string instanceStr() const; public: + void purityCheck(); static void relinkOneLink(AstNode*& pointpr, AstNode* newp); // cppcheck-suppress functionConst static void debugTreeChange(const AstNode* nodep, const char* prefix, int lineno, bool next); diff --git a/src/V3LinkInc.cpp b/src/V3LinkInc.cpp index 0dd3fc3b4..a72ff6e1c 100644 --- a/src/V3LinkInc.cpp +++ b/src/V3LinkInc.cpp @@ -27,6 +27,7 @@ // Create a temporary __VIncrementX variable, assign the value of // of the current variable (after the operation) to it. Substitute // The original variable with the temporary one in the statement. +// // prepost_stmt_visit // PREADD/PRESUB/POSTADD/POSTSUB // Increment/decrement the current variable by the given value. @@ -34,6 +35,12 @@ // the pre/post operations are treated equally and there is no // need for a temporary variable. // +// prepost_stmt_sel_visit +// For e.g. 'array[something_with_side_eff]++', common in UVM etc +// PREADD/PRESUB/POSTADD/POSTSUB +// Create temporary with array index. +// Increment/decrement using index of the temporary. +// //************************************************************************* #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT @@ -205,12 +212,69 @@ class LinkIncVisitor final : public VNVisitor { void visit(AstPropSpec* nodep) override { unsupported_visit(nodep); } void prepost_visit(AstNodeTriop* nodep) { // Check if we are underneath a statement - if (!m_insStmtp) { - prepost_stmt_visit(nodep); + AstSelBit* const selbitp = VN_CAST(nodep->thsp(), SelBit); + if (!m_insStmtp && selbitp && VN_IS(selbitp->fromp(), NodeVarRef) + && !selbitp->bitp()->isPure()) { + prepost_stmt_sel_visit(nodep); } else { - prepost_expr_visit(nodep); + // Purity check was deferred at creation in verilog.y, check now + nodep->thsp()->purityCheck(); + if (!m_insStmtp) { + prepost_stmt_visit(nodep); + } else { + prepost_expr_visit(nodep); + } } } + void prepost_stmt_sel_visit(AstNodeTriop* nodep) { + // Special case array[something]++, see comments at file top + // if (debug() >= 9) nodep->dumpTree("-pp-stmt-sel-in: "); + iterateChildren(nodep); + AstConst* const constp = VN_AS(nodep->lhsp(), Const); + UASSERT_OBJ(nodep, constp, "Expecting CONST"); + AstConst* const newconstp = constp->cloneTree(true); + + AstSelBit* const rdSelbitp = VN_CAST(nodep->rhsp(), SelBit); + AstNodeExpr* const rdFromp = rdSelbitp->fromp()->unlinkFrBack(); + AstNodeExpr* const rdBitp = rdSelbitp->bitp()->unlinkFrBack(); + AstSelBit* const wrSelbitp = VN_CAST(nodep->thsp(), SelBit); + AstNodeExpr* const wrFromp = wrSelbitp->fromp()->unlinkFrBack(); + + // Prepare a temporary variable + FileLine* const fl = nodep->fileline(); + const string name = "__VincIndex"s + cvtToStr(++m_modIncrementsNum); + AstVar* const varp = new AstVar{ + fl, VVarType::BLOCKTEMP, name, VFlagChildDType{}, + new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, rdBitp->cloneTree(true)}}; + if (m_ftaskp) varp->funcLocal(true); + + // Declare the variable + insertOnTop(varp); + + // Define what operation will we be doing + AstAssign* const varAssignp + = new AstAssign{fl, new AstVarRef{fl, varp, VAccess::WRITE}, rdBitp}; + AstNode* const newp = varAssignp; + + AstNodeExpr* const valuep + = new AstSelBit{fl, rdFromp, new AstVarRef{fl, varp, VAccess::READ}}; + AstNodeExpr* const storeTop + = new AstSelBit{fl, wrFromp, new AstVarRef{fl, varp, VAccess::READ}}; + + AstAssign* assignp; + if (VN_IS(nodep, PreSub) || VN_IS(nodep, PostSub)) { + assignp = new AstAssign{nodep->fileline(), storeTop, + new AstSub{nodep->fileline(), valuep, newconstp}}; + } else { + assignp = new AstAssign{nodep->fileline(), storeTop, + new AstAdd{nodep->fileline(), valuep, newconstp}}; + } + newp->addNext(assignp); + + // if (debug() >= 9) newp->dumpTreeAndNext("-pp-stmt-sel-new: "); + nodep->replaceWith(newp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + } void prepost_stmt_visit(AstNodeTriop* nodep) { iterateChildren(nodep); AstConst* const constp = VN_AS(nodep->lhsp(), Const); diff --git a/src/verilog.y b/src/verilog.y index 9e292785e..8e6a48ea7 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -3832,17 +3832,21 @@ inc_or_dec_expression: // ==IEEE: inc_or_dec_expression // // Need fexprScope instead of variable_lvalue to prevent conflict ~l~exprScope yP_PLUSPLUS { $$ = $1; $$ = new AstPostAdd{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"}, - $1, $1->cloneTreePure(true)}; } + // Purity checked in V3LinkInc + $1, $1->cloneTree(true)}; } | ~l~exprScope yP_MINUSMINUS { $$ = $1; $$ = new AstPostSub{$2, new AstConst{$2, AstConst::StringToParse{}, "'b1"}, - $1, $1->cloneTreePure(true)}; } + // Purity checked in V3LinkInc + $1, $1->cloneTree(true)}; } // // Need expr instead of variable_lvalue to prevent conflict | yP_PLUSPLUS expr { $$ = $1; $$ = new AstPreAdd{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"}, - $2, $2->cloneTreePure(true)}; } + // Purity checked in V3LinkInc + $2, $2->cloneTree(true)}; } | yP_MINUSMINUS expr { $$ = $1; $$ = new AstPreSub{$1, new AstConst{$1, AstConst::StringToParse{}, "'b1"}, - $2, $2->cloneTreePure(true)}; } + // Purity checked in V3LinkInc + $2, $2->cloneTree(true)}; } ; finc_or_dec_expression: // ==IEEE: inc_or_dec_expression diff --git a/test_regress/t/t_array_index_side.py b/test_regress/t/t_array_index_side.py new file mode 100755 index 000000000..dbdaf4551 --- /dev/null +++ b/test_regress/t/t_array_index_side.py @@ -0,0 +1,18 @@ +#!/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('simulator_st') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_array_index_side.v b/test_regress/t/t_array_index_side.v new file mode 100644 index 000000000..679edbe70 --- /dev/null +++ b/test_regress/t/t_array_index_side.v @@ -0,0 +1,54 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d (%s !== %s)\n", `__FILE__,`__LINE__, (gotv), (expv), `"gotv`", `"expv`"); `stop; end while(0); + +class Cls; + int m_index; + + function automatic int get_index(); + int rtn; + rtn = m_index; + ++m_index; +`ifdef VERILATOR + return $c(rtn); // Avoid optimizations +`else + return rtn; +`endif + endfunction +endclass + +module t (/*AUTOARG*/); + + Cls cls; + int array[10]; + + initial begin + cls = new; + // Common UVM construct 'id_cnt[get_id()]++;' + // Properly avoid/handle SIDEEFF warnings + cls.m_index = 5; + array[5] = 50; + array[6] = 60; + array[7] = 70; + array[8] = 80; + + array[cls.get_index()]++; + `checkd(array[5], 51); + array[cls.get_index()]++; + `checkd(array[6], 61); + + ++array[cls.get_index()]; + `checkd(array[7], 71); + ++array[cls.get_index()]; + `checkd(array[8], 81); + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule diff --git a/test_regress/t/t_stmt_incr_unsup.out b/test_regress/t/t_stmt_incr_unsup.out index c7c313981..a7ac92291 100644 --- a/test_regress/t/t_stmt_incr_unsup.out +++ b/test_regress/t/t_stmt_incr_unsup.out @@ -1,11 +1,17 @@ -%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:12: Expression side effect may be mishandled +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:31: Expression side effect may be mishandled : ... Suggest use a temporary variable in place of this expression - 17 | arr[postincrement_i()]++; - | ^ + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^ ... For warning description see https://verilator.org/warn/SIDEEFFECT?v=latest ... Use "/* verilator lint_off SIDEEFFECT */" and lint_on around source to disable this message. %Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:13: Expression side effect may be mishandled + : ... note: In instance 't' : ... Suggest use a temporary variable in place of this expression - 17 | arr[postincrement_i()]++; + 17 | arr[postincrement_i()][postincrement_i()]++; | ^~~~~~~~~~~~~~~ +%Warning-SIDEEFFECT: t/t_stmt_incr_unsup.v:17:32: Expression side effect may be mishandled + : ... note: In instance 't' + : ... Suggest use a temporary variable in place of this expression + 17 | arr[postincrement_i()][postincrement_i()]++; + | ^~~~~~~~~~~~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_stmt_incr_unsup.v b/test_regress/t/t_stmt_incr_unsup.v index e1682331a..09e258028 100644 --- a/test_regress/t/t_stmt_incr_unsup.v +++ b/test_regress/t/t_stmt_incr_unsup.v @@ -12,9 +12,9 @@ endfunction module t; initial begin - int arr [1:0] = {0, 0}; + int arr [3][3] = {{1, 2, 3}, {4, 5, 6}, {7, 8, 9}}; i = 0; - arr[postincrement_i()]++; + arr[postincrement_i()][postincrement_i()]++; $display("Value: %d", i); end endmodule From 0380a36c7644365a15b2c94d8c72dbba3374c7e1 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Thu, 9 Jan 2025 14:33:38 +0100 Subject: [PATCH 25/93] Fix constrained random for > 64-bit associative arrays (#5670) (#5682) --- include/verilated_random.cpp | 14 +- include/verilated_random.h | 155 +++++++++++++----- include/verilated_types.h | 1 + src/V3Randomize.cpp | 35 ++-- test_regress/t/t_constraint_assoc_arr_bad.out | 9 +- test_regress/t/t_constraint_assoc_arr_bad.v | 10 +- test_regress/t/t_constraint_assoc_arr_basic.v | 34 +++- test_regress/t/t_constraint_assoc_arr_wide.py | 21 +++ test_regress/t/t_constraint_assoc_arr_wide.v | 115 +++++++++++++ 9 files changed, 312 insertions(+), 82 deletions(-) mode change 100644 => 100755 test_regress/t/t_constraint_assoc_arr_bad.v create mode 100755 test_regress/t/t_constraint_assoc_arr_wide.py create mode 100755 test_regress/t/t_constraint_assoc_arr_wide.v diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 3dc3b24b0..a525df5c4 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -282,6 +282,7 @@ std::string parseNestedSelect(const std::string& nested_select_expr, indices.push_back(idx); return name; } + //====================================================================== // VlRandomizer:: Methods @@ -467,10 +468,19 @@ bool VlRandomizer::parseSolution(std::iostream& f) { "hex_index contains invalid format"); continue; } - const long long index = std::stoll(hex_index.substr(start + 2), nullptr, 16); - oss << "[" << index << "]"; + std::string trimmed_hex = hex_index.substr(start + 2); + + if (trimmed_hex.size() <= 8) { // Small numbers: <= 32 bits + // Convert to decimal and output directly + oss << "[" << std::to_string(std::stoll(trimmed_hex, nullptr, 16)) << "]"; + } else { // Large numbers: > 32 bits + // Trim leading zeros and handle empty case + trimmed_hex.erase(0, trimmed_hex.find_first_not_of('0')); + oss << "[" << (trimmed_hex.empty() ? "0" : trimmed_hex) << "]"; + } } const std::string indexed_name = oss.str(); + const auto it = std::find_if(m_arr_vars.begin(), m_arr_vars.end(), [&indexed_name](const auto& entry) { return entry.second->m_name == indexed_name; diff --git a/include/verilated_random.h b/include/verilated_random.h index 90d92aa9e..24ba84385 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -27,8 +27,10 @@ #include "verilated.h" +#include #include #include +#include //============================================================================= // VlRandomExpr and subclasses represent expressions for the constraint solver. @@ -38,10 +40,10 @@ public: m_name; // Name of the array variable, including index notation (e.g., arr[2][1]) void* const m_datap; // Reference to the array variable data const int m_index; // Flattened (1D) index of the array element - const std::vector m_indices; // Multi-dimensional indices of the array element + const std::vector m_indices; // Multi-dimensional indices of the array element const std::vector m_idxWidths; // Multi-dimensional indices' bit widths - ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, + ArrayInfo(const std::string& name, void* datap, int index, const std::vector& indices, const std::vector& idxWidths) : m_name(name) , m_datap(datap) @@ -110,16 +112,31 @@ public: return nullptr; } } - void emitSelect(std::ostream& s, const std::vector& indices, + void emitHexs(std::ostream& s, const std::vector& indices, const size_t bit_width, + size_t idx) const { + for (int j = bit_width - 4; j >= 0; j -= 4) { + s << "0123456789abcdef"[(indices[idx] >> j) & 0xf]; + } + } + void emitSelect(std::ostream& s, const std::vector& indices, const std::vector& idxWidths) const { - for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select "; + const size_t num_indices = idxWidths.size(); + size_t wide_size = 0; + + for (size_t idx = 0; idx < num_indices; ++idx) s << "(select "; s << name(); - for (size_t idx = 0; idx < indices.size(); ++idx) { - s << " #x"; + + for (size_t idx = 0; idx < num_indices; ++idx) { const size_t bit_width = idxWidths[idx]; - for (int j = bit_width - 4; j >= 0; j -= 4) { - s << "0123456789abcdef"[(indices[idx] >> j) & 0xf]; + s << " #x"; + + const size_t emit_count = (bit_width > 32) ? (idxWidths[idx] / 32) : 1; + + for (size_t i = 0; i < emit_count; ++i) { + emitHexs(s, indices, (bit_width > 32) ? 32 : bit_width, wide_size + i); } + + wide_size += (idxWidths[idx] > 32) ? (idxWidths[idx] / 32) : 1; s << ")"; } } @@ -129,7 +146,7 @@ public: const std::string indexed_name = name() + std::to_string(i); const auto it = m_arrVarsRefp->find(indexed_name); if (it != m_arrVarsRefp->end()) { - const std::vector& indices = it->second->m_indices; + const std::vector& indices = it->second->m_indices; const std::vector& idxWidths = it->second->m_idxWidths; emitSelect(s, indices, idxWidths); } else { @@ -165,7 +182,7 @@ public: const std::string indexed_name = name() + std::to_string(j); const auto it = m_arrVarsRefp->find(indexed_name); if (it != m_arrVarsRefp->end()) { - const std::vector& indices = it->second->m_indices; + const std::vector& indices = it->second->m_indices; const std::vector& idxWidths = it->second->m_idxWidths; emitSelect(s, indices, idxWidths); } else { @@ -176,7 +193,6 @@ public: }; //============================================================================= // VlRandomizer is the object holding constraints and variable references. - class VlRandomizer final { // MEMBERS std::vector m_constraints; // Solver-dependent constraints @@ -200,45 +216,89 @@ public: bool next(VlRNG& rngr); template - typename std::enable_if::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + typename std::enable_if::value && (sizeof(T_Key) <= 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, const std::string& base_name, size_t& idx_width) { - integral_index = static_cast(key); - indexed_name = base_name + "[" + std::to_string(integral_index) + "]"; + integral_index.push_back(static_cast(key)); + indexed_name + = base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]"; idx_width = sizeof(T_Key) * 8; } template - typename std::enable_if::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + typename std::enable_if::value && (sizeof(T_Key) > 4)>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, const std::string& base_name, size_t& idx_width) { - integral_index = string_to_integral(key); - indexed_name = base_name + "[" + std::to_string(integral_index) + "]"; - idx_width = 64; // 64-bit mask + constexpr size_t segment_bits = 32; + constexpr T_Key mask = (static_cast(1) << segment_bits) - 1; + integral_index.push_back(static_cast(key >> segment_bits)); + integral_index.push_back(static_cast(key & mask)); + + std::ostringstream hex_stream; + hex_stream << std::hex << key; + std::string index_string = hex_stream.str(); + index_string.erase(0, index_string.find_first_not_of('0')); + index_string = index_string.empty() ? "0" : index_string; + + indexed_name = base_name + "[" + index_string + "]"; + + idx_width = sizeof(T_Key) * 8; + } + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + std::ostringstream hex_stream; + for (size_t i = key.size(); i > 0; --i) { + const size_t segment_value = key.at(i - 1); + hex_stream << std::hex << segment_value; + integral_index.push_back(segment_value); + } + std::string index_string = hex_stream.str(); + index_string.erase(0, index_string.find_first_not_of('0')); + index_string = index_string.empty() ? "0" : index_string; + + indexed_name = base_name + "[" + index_string + "]"; + idx_width = key.size() * 32; + } + template + typename std::enable_if::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, + const std::string& base_name, size_t& idx_width) { + // Convert the input string to its ASCII hexadecimal representation + std::ostringstream oss; + for (unsigned char c : key) { + oss << std::hex << std::setw(2) << std::setfill('0') << static_cast(c); + } + std::string hex_str = oss.str(); + // Ensure the hex string is exactly 128 bits (32 hex characters) + hex_str = hex_str.size() > 32 ? hex_str.substr(0, 32) + : std::string(32 - hex_str.size(), '0') + hex_str; + + // Split the hex string into 4 segments (32-bit per segment) + integral_index.clear(); + for (size_t i = 0; i < hex_str.size(); i += 8) { + integral_index.push_back(std::stoul(hex_str.substr(i, 8), nullptr, 16)); + } + + indexed_name = base_name + "[" + + (hex_str.find_first_not_of('0') == std::string::npos + ? "0" + : hex_str.substr(hex_str.find_first_not_of('0'))) + + "]"; + + idx_width = 128; } template typename std::enable_if::value - && !std::is_same::value>::type - process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index, + && !std::is_same::value + && !VlIsVlWide::value>::type + process_key(const T_Key& key, std::string& indexed_name, std::vector& integral_index, const std::string& base_name, size_t& idx_width) { VL_FATAL_MT(__FILE__, __LINE__, "randomize", "Unsupported: Only integral and string index of associative array is " "supported currently."); } - uint64_t string_to_integral(const std::string& str) { - uint64_t result = 0; - for (char c : str) { result = (result << 8) | static_cast(c); } - -#ifdef VL_DEBUG - if (seen_values.count(result) > 0 && seen_values[result] != str) - VL_WARN_MT(__FILE__, __LINE__, "randomize", - "Conflict detected: Different strings mapped to the same 64-bit index."); - seen_values[result] = str; -#endif - - return result; - } - template void write_var(T& var, int width, const char* name, int dimension, std::uint32_t randmodeIdx = std::numeric_limits::max()) { @@ -296,14 +356,14 @@ public: template void record_arr_table(T& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector idxWidths) { const std::string key = generateKey(name, idx); m_arr_vars[key] = std::make_shared(name, &var, idx, indices, idxWidths); ++idx; } template void record_arr_table(VlQueue& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector idxWidths) { if ((dimension > 0) && (var.size() != 0)) { idxWidths.push_back(32); for (size_t i = 0; i < var.size(); ++i) { @@ -316,7 +376,7 @@ public: } template void record_arr_table(VlUnpacked& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector idxWidths) { if ((dimension > 0) && (N_Depth != 0)) { idxWidths.push_back(32); for (size_t i = 0; i < N_Depth; ++i) { @@ -330,20 +390,27 @@ public: } template void record_arr_table(VlAssocArray& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + std::vector indices, std::vector idxWidths) { if ((dimension > 0) && (var.size() != 0)) { for (auto it = var.begin(); it != var.end(); ++it) { const T_Key& key = it->first; const T_Value& value = it->second; + std::string indexed_name; - size_t integral_index; - size_t idx_width; + std::vector integral_index; + size_t idx_width = 0; + process_key(key, indexed_name, integral_index, name, idx_width); + + // Update indices and widths idxWidths.push_back(idx_width); - indices.push_back(integral_index); + indices.insert(indices.end(), integral_index.begin(), integral_index.end()); + record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths); + + // Cleanup indices and widths idxWidths.pop_back(); - indices.pop_back(); + indices.resize(indices.size() - integral_index.size()); } } } diff --git a/include/verilated_types.h b/include/verilated_types.h index 84caafa27..851366b95 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -451,6 +451,7 @@ struct VlWide final { // METHODS const EData& at(size_t index) const { return m_storage[index]; } EData& at(size_t index) { return m_storage[index]; } + size_t size() const { return N_Words; } WData* data() { return &m_storage[0]; } const WData* data() const { return &m_storage[0]; } bool operator<(const VlWide& rhs) const { diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 96c3f6945..0fdb58a16 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -710,15 +710,19 @@ class ConstraintExprVisitor final : public VNVisitor { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); if (VN_IS(nodep->bitp(), CvtPackString)) { - // Extract and truncate the string index to fit within 64 bits AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString); + const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width(); + if (stringSize > 128) { + stringp->v3warn( + CONSTRAINTIGN, + "Unsupported: Constrained randomization of associative array keys of " + << stringSize << "bits, limit is 128 bits"); + } VNRelinker handle; - AstNodeExpr* const strIdxp = new AstSFormatF{ - fl, "#x%16x", false, - new AstAnd{fl, stringp->lhsp()->unlinkFrBack(&handle), - new AstConst(fl, AstConst::Unsized64{}, 0xFFFFFFFFFFFFFFFF)}}; - handle.relink(strIdxp); - editSMT(nodep, nodep->fromp(), strIdxp); + AstNodeExpr* const idxp + = new AstSFormatF{fl, "#x%32x", false, stringp->lhsp()->unlinkFrBack(&handle)}; + handle.relink(idxp); + editSMT(nodep, nodep->fromp(), idxp); } else { VNRelinker handle; const int actual_width = nodep->bitp()->width(); @@ -728,16 +732,10 @@ class ConstraintExprVisitor final : public VNVisitor { fmt = "#x%2x"; } else if (actual_width <= 16) { fmt = "#x%4x"; - } else if (actual_width <= 32) { - fmt = "#x%8x"; - } else if (actual_width <= 64) { - fmt = "#x%16x"; } else { - nodep->v3warn(CONSTRAINTIGN, - "Unsupported: Associative array index " - "widths of more than 64 bits during constraint randomization."); - return; + fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; } + AstNodeExpr* const idxp = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; handle.relink(idxp); @@ -1450,11 +1448,8 @@ class RandomizeVisitor final : public VNVisitor { AstNodeStmt* stmtsp = nullptr; auto createLoopIndex = [&](AstNodeDType* tempDTypep) { if (VN_IS(tempDTypep, AssocArrayDType)) { - return new AstVar{ - fl, VVarType::VAR, uniqueNamep->get(""), - dtypep->findBasicDType( - ((AstBasicDType*)VN_AS(tempDTypep, AssocArrayDType)->keyDTypep()) - ->keyword())}; + return new AstVar{fl, VVarType::VAR, uniqueNamep->get(""), + VN_AS(tempDTypep, AssocArrayDType)->keyDTypep()}; } return new AstVar{fl, VVarType::VAR, uniqueNamep->get(""), dtypep->findBasicDType(VBasicDTypeKwd::UINT32)}; diff --git a/test_regress/t/t_constraint_assoc_arr_bad.out b/test_regress/t/t_constraint_assoc_arr_bad.out index a048591fc..0e919e3b9 100644 --- a/test_regress/t/t_constraint_assoc_arr_bad.out +++ b/test_regress/t/t_constraint_assoc_arr_bad.out @@ -1,9 +1,6 @@ -%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:14:22: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. - 14 | bit_index_arr[79'd66] == 65; - | ^ +%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:12:20: Unsupported: Constrained randomization of associative array keys of 144bits, limit is 128 bits + 12 | string_arr["a_very_long_string"] == 65; + | ^~~~~~~~~~~~~~~~~~~~ ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. -%Warning-CONSTRAINTIGN: t/t_constraint_assoc_arr_bad.v:15:24: Unsupported: Associative array index widths of more than 64 bits during constraint randomization. - 15 | logic_index_arr[65'd3] == 70; - | ^ %Error: Exiting due to diff --git a/test_regress/t/t_constraint_assoc_arr_bad.v b/test_regress/t/t_constraint_assoc_arr_bad.v old mode 100644 new mode 100755 index 2050f2643..7ffeba96a --- a/test_regress/t/t_constraint_assoc_arr_bad.v +++ b/test_regress/t/t_constraint_assoc_arr_bad.v @@ -4,19 +4,15 @@ // any use, without warranty, 2024 by PlanV GmbH. // SPDX-License-Identifier: CC0-1.0 - class AssocArrayWarningTest; - rand int bit_index_arr [bit[78:0]]; - rand int logic_index_arr [logic[64:0]]; + rand int string_arr [string]; constraint c { - bit_index_arr[79'd66] == 65; - logic_index_arr[65'd3] == 70; + string_arr["a_very_long_string"] == 65; } function new(); - bit_index_arr = '{79'd66:0}; - logic_index_arr = '{65'd3:0}; + string_arr["a_very_long_string"] = 0; endfunction endclass diff --git a/test_regress/t/t_constraint_assoc_arr_basic.v b/test_regress/t/t_constraint_assoc_arr_basic.v index 105cdb96a..1cd43562d 100644 --- a/test_regress/t/t_constraint_assoc_arr_basic.v +++ b/test_regress/t/t_constraint_assoc_arr_basic.v @@ -147,11 +147,33 @@ class constrained_2d_associative_array; /* verilator lint_off SIDEEFFECT */ endclass +class AssocArray64bitMoreTest; + + rand int bit_index_arr [bit[78:0]]; + rand int logic_index_arr [logic[64:0]]; + + constraint c { + bit_index_arr[79'd66] == 65; + logic_index_arr[65'd3] == 70; + } + function new(); + bit_index_arr = '{79'd66:0}; + logic_index_arr = '{65'd3:0}; + endfunction + function void self_check(); + if (bit_index_arr[79'd66] != 65) $stop; + if (logic_index_arr[65'd3] != 70) $stop; + endfunction + +endclass + module t_constraint_assoc_arr_basic; constrained_associative_array_basic my_array; constrained_1d_associative_array my_1d_array; constrained_2d_associative_array my_2d_array; + + AssocArray64bitMoreTest test_obj; int success; initial begin @@ -165,10 +187,16 @@ module t_constraint_assoc_arr_basic; if (success == 0) $stop; my_1d_array.self_check(); - my_1d_array = new(); - success = my_1d_array.randomize(); + my_2d_array = new(); + success = my_2d_array.randomize(); if (success == 0) $stop; - my_1d_array.self_check(); + my_2d_array.self_check(); + + test_obj = new(); + repeat(2) begin + success = test_obj.randomize(); + if (success != 1) $stop; + end // my_1d_array.debug_display(); // my_2d_array.debug_display(); diff --git a/test_regress/t/t_constraint_assoc_arr_wide.py b/test_regress/t/t_constraint_assoc_arr_wide.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_wide.py @@ -0,0 +1,21 @@ +#!/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('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_assoc_arr_wide.v b/test_regress/t/t_constraint_assoc_arr_wide.v new file mode 100755 index 000000000..52dca3fc2 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_wide.v @@ -0,0 +1,115 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class AssocIntegralWide; + + rand bit [31:0] assoc_array[bit[64:0]]; + rand bit [31:0] assoc_array_128[bit[128:0]]; + rand bit [31:0] assoc_array_2d[bit[64:0]][bit[128:0]]; + + constraint valid_entries { + assoc_array[65'd6] == 32'd8; + assoc_array[65'h1FFFFFFFFFFFFFFFF] == 32'hDEADBEEF; + + assoc_array_128[129'd6] == 32'd16; + assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] == 32'hCAFEBABE; + + assoc_array_2d[65'd6][129'd6] == 32'd32; + assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] == 32'hBADF00D; + } + + // Constructor to initialize arrays + function new(); + assoc_array[65'd0] = 32'd0; + assoc_array[65'd6] = 32'd0; + assoc_array[65'h1FFFFFFFFFFFFFFFF] = 32'd0; + + assoc_array_128[129'd0] = 32'd0; + assoc_array_128[129'd6] = 32'd0; + assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] = 32'd0; + + assoc_array_2d[65'd6][129'd6] = 32'd0; + assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] = 32'd0; + endfunction + + // Self-check function to verify constraints + function void self_check(); + if (assoc_array[65'd6] != 32'd8) + $stop; + if (assoc_array[65'h1FFFFFFFFFFFFFFFF] != 32'hDEADBEEF) + $stop; + + if (assoc_array_128[129'd6] != 32'd16) + $stop; + if (assoc_array_128[129'h1FFFFFFFFFFFFFFFFFFFFFFFF] != 32'hCAFEBABE) + $stop; + + if (assoc_array_2d[65'd6][129'd6] != 32'd32) + $stop; + if (assoc_array_2d[65'h1FFFFFFFFFFFFFFFF][129'h1FFFFFFFFFFFFFFFFFFFFFFFF] != 32'hBADF00D) + $stop; + endfunction + +endclass + +class AssocStringWide; + + rand bit [31:0] array_32[string]; + rand bit [31:0] array_64[string]; + rand bit [31:0] array_96[string]; + rand bit [31:0] array_128[string]; + + constraint valid_entries { + // <= 32 bits + array_32["pv"] == 32'd10; + // > 32 and <= 64 bits + array_64["verilog"] == 32'd20; + // > 32 and <= 64 bits + array_96["verilator"] == 32'd30; + // > 64 and <= 96 bits + array_128["systemverilog"] == 32'd40; + } + + function new(); + array_32["pv"] = 32'd0; + array_64["verilog"] = 32'd0; + array_96["verilator"] = 32'd0; + array_128["systemverilog"] = 32'd0; + endfunction + + function void self_check(); + if (array_32["pv"] != 32'd10) $stop; + if (array_64["verilog"] != 32'd20) $stop; + if (array_96["verilator"] != 32'd30) $stop; + if (array_128["systemverilog"] != 32'd40) $stop; + endfunction + +endclass + + +module t_constraint_assoc_arr_wide; + + AssocIntegralWide integral_wide; + AssocStringWide string_wide; + + int success; + initial begin + + integral_wide = new(); + string_wide = new(); + + success = integral_wide.randomize(); + if (success != 1) $stop; + integral_wide.self_check(); + + success = string_wide.randomize(); + if (success != 1) $stop; + string_wide.self_check(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule From 052812bb87ad5312ec2e2e624c588cae9d20a585 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Thu, 9 Jan 2025 14:40:32 +0100 Subject: [PATCH 26/93] Fix `BLKSEQ` on suspendable processes (#5722) Signed-off-by: Krzysztof Bieganski --- src/V3Active.cpp | 18 ++++++++++-------- test_regress/t/t_timing_clkgen2.py | 2 +- test_regress/t/t_timing_clkgen2.v | 14 ++++---------- 3 files changed, 15 insertions(+), 19 deletions(-) diff --git a/src/V3Active.cpp b/src/V3Active.cpp index f9c419314..9352737a6 100644 --- a/src/V3Active.cpp +++ b/src/V3Active.cpp @@ -349,7 +349,7 @@ public: class ActiveDlyVisitor final : public VNVisitor { public: - enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL }; + enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE }; private: // MEMBERS @@ -358,7 +358,7 @@ private: // VISITORS void visit(AstAssignDly* nodep) override { // Non-blocking assignments are OK in sequential processes - if (m_check == CT_SEQ) return; + if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return; // Issue appropriate warning if (m_check == CT_INITIAL) { @@ -477,10 +477,8 @@ class ActiveVisitor final : public VNVisitor { AstActive* const wantactivep = !m_clockedProcess ? m_namer.getSpecialActive(nodep->fileline()) : oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp) - : m_namer.getSpecialActive(nodep->fileline()); - - // Delete sensitivity list - if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp); + // Clocked, no sensitivity lists, it's a suspendable, put it in initial + : m_namer.getSpecialActive(nodep->fileline()); // Move node to new active nodep->unlinkFrBack(); @@ -488,10 +486,14 @@ class ActiveVisitor final : public VNVisitor { // Warn and convert any delayed assignments { - ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ - : ActiveDlyVisitor::CT_COMB}; + ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB + : oldsensesp ? ActiveDlyVisitor::CT_SEQ + : ActiveDlyVisitor::CT_SUSPENDABLE}; } + // Delete sensitivity list + if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp); + // check combinational processes for latches if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) { const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH}; diff --git a/test_regress/t/t_timing_clkgen2.py b/test_regress/t/t_timing_clkgen2.py index a4e75e7ad..500334384 100755 --- a/test_regress/t/t_timing_clkgen2.py +++ b/test_regress/t/t_timing_clkgen2.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(verilator_flags2=["--exe --main --timing"]) +test.compile(verilator_flags2=["--exe --main --timing -Wwarn-BLKSEQ"]) test.execute() diff --git a/test_regress/t/t_timing_clkgen2.v b/test_regress/t/t_timing_clkgen2.v index c1ec90fb1..4df0b2261 100644 --- a/test_regress/t/t_timing_clkgen2.v +++ b/test_regress/t/t_timing_clkgen2.v @@ -17,24 +17,18 @@ module t; int cnt2 = 0; always #4 clk = ~clk; - always @(negedge clk) begin - cnt1++; - `WRITE_VERBOSE(("[%0t] NEG clk (%b)\n", $time, clk)); - end always @(posedge clk) begin - cnt1++; - `WRITE_VERBOSE(("[%0t] POS clk (%b)\n", $time, clk)); + cnt1 <= cnt1 + 1; + `WRITE_VERBOSE(("[%0t] clk (%b)\n", $time, clk)); end assign #2 clk_inv = ~clk; initial forever begin @(posedge clk_inv) cnt2++; - `WRITE_VERBOSE(("[%0t] POS clk_inv (%b)\n", $time, clk_inv)); - @(negedge clk_inv) cnt2++; - `WRITE_VERBOSE(("[%0t] NEG clk_inv (%b)\n", $time, clk_inv)); + `WRITE_VERBOSE(("[%0t] clk_inv (%b)\n", $time, clk_inv)); end - initial #41 begin + initial #81 begin if (cnt1 != 10 && cnt2 != 10) $stop; $write("*-* All Finished *-*\n"); $finish; From 69dce205d387ea9f633349424e16a69ca88e4ddb Mon Sep 17 00:00:00 2001 From: Krzysztof Starecki Date: Fri, 10 Jan 2025 01:04:26 +0100 Subject: [PATCH 27/93] Support multidimensional array access via VPI (#2812) (#5573) --- include/verilated.cpp | 32 +- include/verilated.h | 2 +- include/verilated_dpi.cpp | 8 +- include/verilated_sym_props.h | 112 +++-- include/verilated_vpi.cpp | 670 ++++++++++++++++-------------- src/V3AstNodes.cpp | 55 ++- src/V3EmitCSyms.cpp | 39 +- test_regress/t/t_scope_map.cpp | 6 +- test_regress/t/t_vpi_dump.out | 16 +- test_regress/t/t_vpi_get.cpp | 56 +-- test_regress/t/t_vpi_memory.cpp | 54 +-- test_regress/t/t_vpi_multidim.cpp | 462 ++++++++++++++++++++ test_regress/t/t_vpi_multidim.py | 22 + test_regress/t/t_vpi_multidim.v | 44 ++ test_regress/t/t_vpi_var.cpp | 16 +- 15 files changed, 1101 insertions(+), 493 deletions(-) create mode 100644 test_regress/t/t_vpi_multidim.cpp create mode 100755 test_regress/t/t_vpi_multidim.py create mode 100644 test_regress/t/t_vpi_multidim.v diff --git a/include/verilated.cpp b/include/verilated.cpp index a99d97dee..ecf7e8d73 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3289,7 +3289,7 @@ uint32_t VerilatedVarProps::entSize() const VL_MT_SAFE { case VLVT_UINT16: size = sizeof(SData); break; case VLVT_UINT32: size = sizeof(IData); break; case VLVT_UINT64: size = sizeof(QData); break; - case VLVT_WDATA: size = VL_WORDS_I(packed().elements()) * sizeof(IData); break; + case VLVT_WDATA: size = VL_WORDS_I(entBits()) * sizeof(IData); break; default: size = 0; break; // LCOV_EXCL_LINE } return size; @@ -3308,7 +3308,7 @@ void* VerilatedVarProps::datapAdjustIndex(void* datap, int dim, int indx) const uint8_t* bytep = reinterpret_cast(datap); // If on index 1 of a 2 index array, then each index 1 is index2sz*entsz size_t slicesz = entSize(); - for (int d = dim + 1; d <= m_udims; ++d) slicesz *= elements(d); + for (int d = dim + 1; d <= udims(); ++d) slicesz *= elements(d); bytep += indxAdj * slicesz; return bytep; } @@ -3369,32 +3369,28 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ } void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE { + VerilatedVarType vltype, int vlflags, int udims, int pdims ...) VL_MT_UNSAFE { // Grab dimensions // In the future we may just create a large table at emit time and // statically construct from that. if (!finalize) return; if (!m_varsp) m_varsp = new VerilatedVarNameMap; - VerilatedVar var(namep, datap, vltype, static_cast(vlflags), dims, isParam); + VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, isParam); va_list ap; - va_start(ap, dims); - for (int i = 0; i < dims; ++i) { + va_start(ap, pdims); + for (int i = 0; i < udims; ++i) { const int msb = va_arg(ap, int); const int lsb = va_arg(ap, int); - if (i == 0) { - var.m_packed.m_left = msb; - var.m_packed.m_right = lsb; - } else if (i >= 1 && i <= var.udims()) { - var.m_unpacked[i - 1].m_left = msb; - var.m_unpacked[i - 1].m_right = lsb; - } else { - // We could have a linked list of ranges, but really this whole thing needs - // to be generalized to support structs and unions, etc. - const std::string msg = "Unsupported multi-dimensional public varInsert: "s + namep; - VL_FATAL_MT(__FILE__, __LINE__, "", msg.c_str()); - } + var.m_unpacked[i].m_left = msb; + var.m_unpacked[i].m_right = lsb; + } + for (int i = 0; i < pdims; ++i) { + const int msb = va_arg(ap, int); + const int lsb = va_arg(ap, int); + var.m_packed[i].m_left = msb; + var.m_packed[i].m_right = lsb; } va_end(ap); diff --git a/include/verilated.h b/include/verilated.h index cee813c31..baccd0610 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -726,7 +726,7 @@ public: // But internals only - called from VerilatedModule's const Type& type) VL_MT_UNSAFE; void exportInsert(int finalize, const char* namep, void* cb) VL_MT_UNSAFE; void varInsert(int finalize, const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int dims, ...) VL_MT_UNSAFE; + VerilatedVarType vltype, int vlflags, int udims, int pdims, ...) VL_MT_UNSAFE; // ACCESSORS const char* name() const VL_MT_SAFE_POSTINIT { return m_namep; } const char* identifier() const VL_MT_SAFE_POSTINIT { return m_identifierp; } diff --git a/include/verilated_dpi.cpp b/include/verilated_dpi.cpp index 04a926646..37b8a878e 100644 --- a/include/verilated_dpi.cpp +++ b/include/verilated_dpi.cpp @@ -289,7 +289,7 @@ static void _vl_svGetBitArrElemVecVal(svBitVecVal* d, const svOpenArrayHandle s, } case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) d[i] = wdatap[i]; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) d[i] = wdatap[i]; return; } default: // LCOV_EXCL_START // Errored earlier @@ -328,7 +328,7 @@ static void _vl_svGetLogicArrElemVecVal(svLogicVecVal* d, const svOpenArrayHandl } case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) { + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) { d[i].aval = wdatap[i]; d[i].bval = 0; } @@ -354,7 +354,7 @@ static void _vl_svPutBitArrElemVecVal(const svOpenArrayHandle d, const svBitVecV case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1], s[0]); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i]; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i]; return; } default: // LCOV_EXCL_START // Errored earlier @@ -376,7 +376,7 @@ static void _vl_svPutLogicArrElemVecVal(const svOpenArrayHandle d, const svLogic case VLVT_UINT64: *(reinterpret_cast(datap)) = VL_SET_QII(s[1].aval, s[0].aval); break; case VLVT_WDATA: { WDataOutP wdatap = (reinterpret_cast(datap)); - for (int i = 0; i < VL_WORDS_I(varp->packed().elements()); ++i) wdatap[i] = s[i].aval; + for (int i = 0; i < VL_WORDS_I(varp->entBits()); ++i) wdatap[i] = s[i].aval; return; } default: // LCOV_EXCL_START // Errored earlier diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 5298787b0..0d7e5f4a2 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -74,27 +74,41 @@ class VerilatedVarProps VL_NOT_FINAL { const uint32_t m_magic; // Magic number const VerilatedVarType m_vltype; // Data type const VerilatedVarFlags m_vlflags; // Direction - const int m_pdims; // Packed dimensions, 0 = none - const int m_udims; // Unpacked dimensions, 0 = none - VerilatedRange m_packed; // Packed array range std::vector m_unpacked; // Unpacked array ranges - void initUnpacked(const int* ulims) { - for (int i = 0; i < m_udims; ++i) { + std::vector m_packed; // Packed array ranges + VerilatedRange m_packedDpi; // Flattened packed array range + void initUnpacked(int udims, const int* ulims) { + for (int i = 0; i < udims; ++i) { const int uleft = ulims ? ulims[2 * i + 0] : 0; const int uright = ulims ? ulims[2 * i + 1] : 0; m_unpacked.emplace_back(uleft, uright); } } + void initPacked(int pdims, const int* plims) { + int packedSize = 1; + for (int i = 0; i < pdims; ++i) { + const int pleft = plims ? plims[2 * i + 0] : 0; + const int pright = plims ? plims[2 * i + 1] : 0; + m_packed.emplace_back(pleft, pright); + packedSize *= abs(pleft - pright) + 1; + } + if (pdims == 1) { + // Preserve packed array range if the packed component is 1-D + m_packedDpi = m_packed.front(); + } else { + m_packedDpi = VerilatedRange{packedSize - 1, 0}; + } + } // CONSTRUCTORS protected: friend class VerilatedScope; - VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int pdims, int udims) + VerilatedVarProps(VerilatedVarType vltype, VerilatedVarFlags vlflags, int udims, int pdims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags{vlflags} - , m_pdims{pdims} - , m_udims{udims} { - initUnpacked(nullptr); + , m_vlflags{vlflags} { + // Only preallocate the ranges + initUnpacked(udims, nullptr); + initPacked(pdims, nullptr); } public: @@ -103,35 +117,29 @@ public: VerilatedVarProps(VerilatedVarType vltype, int vlflags) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{0} - , m_udims{0} {} + , m_vlflags(VerilatedVarFlags(vlflags)) {} // Need () or GCC 4.8 false warning + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{0} - , m_udims{udims} { - initUnpacked(ulims); + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); } // With packed class Packed {}; - VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr) + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pdims, const int* plims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{1} - , m_udims{0} - , m_packed{pl, pr} {} - VerilatedVarProps(VerilatedVarType vltype, int vlflags, Packed, int pl, int pr, Unpacked, - int udims, const int* ulims) + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initPacked(pdims, plims); + } + VerilatedVarProps(VerilatedVarType vltype, int vlflags, Unpacked, int udims, const int* ulims, + Packed, int pdims, const int* plims) : m_magic{MAGIC} , m_vltype{vltype} - , m_vlflags(VerilatedVarFlags(vlflags)) // Need () or GCC 4.8 false warning - , m_pdims{1} - , m_udims{udims} - , m_packed{pl, pr} { - initUnpacked(ulims); + , m_vlflags(VerilatedVarFlags(vlflags)) { // Need () or GCC 4.8 false warning + initUnpacked(udims, ulims); + initPacked(pdims, plims); } ~VerilatedVarProps() = default; @@ -142,41 +150,56 @@ public: return static_cast(static_cast(m_vlflags) & VLVF_MASK_DIR); } uint32_t entSize() const VL_MT_SAFE; + uint32_t entBits() const VL_MT_SAFE { + uint32_t bits = 1; + for (auto it : m_packed) + bits *= it.elements(); + return bits; + } bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); } // DPI compatible C standard layout bool isDpiCLayout() const { return ((m_vlflags & VLVF_DPI_CLAY) != 0); } - int udims() const VL_MT_SAFE { return m_udims; } - int dims() const { return m_pdims + m_udims; } - const VerilatedRange& packed() const VL_MT_SAFE { return m_packed; } - const VerilatedRange& unpacked() const { return m_unpacked[0]; } - // DPI accessors + int udims() const VL_MT_SAFE { return m_unpacked.size(); } + int pdims() const VL_MT_SAFE { return m_packed.size(); } + int dims() const VL_MT_SAFE { return pdims() + udims(); } + const std::vector& packedRanges() const VL_MT_SAFE { return m_packed; } + const std::vector& unpackedRanges() const VL_MT_SAFE { return m_unpacked; } + const VerilatedRange* range(int dim) const VL_MT_SAFE { + if (dim < udims()) + return &m_unpacked[dim]; + else if (dim < dims()) + return &m_packed[dim - udims()]; + else + return nullptr; + } + // DPI accessors (with packed dimensions flattened!) int left(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.left() + return dim == 0 ? m_packedDpi.left() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].left() : 0; } int right(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.right() + return dim == 0 ? m_packedDpi.right() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].right() : 0; } int low(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.low() + return dim == 0 ? m_packedDpi.low() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].low() : 0; } int high(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.high() + return dim == 0 ? m_packedDpi.high() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].high() : 0; } int increment(int dim) const { - return dim == 0 ? m_packed.increment() + return dim == 0 ? m_packedDpi.increment() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].increment() : 0; } int elements(int dim) const VL_MT_SAFE { - return dim == 0 ? m_packed.elements() + return dim == 0 ? m_packedDpi.elements() : VL_LIKELY(dim >= 1 && dim <= udims()) ? m_unpacked[dim - 1].elements() : 0; } @@ -208,8 +231,7 @@ public: bool magicOk() const { return m_propsp->magicOk(); } VerilatedVarType vltype() const { return m_propsp->vltype(); } bool isDpiStdLayout() const { return m_propsp->isDpiCLayout(); } - const VerilatedRange& packed() const { return m_propsp->packed(); } - const VerilatedRange& unpacked() const { return m_propsp->unpacked(); } + int entBits() const { return m_propsp->entBits(); } int udims() const VL_MT_SAFE { return m_propsp->udims(); } int left(int dim) const VL_MT_SAFE { return m_propsp->left(dim); } int right(int dim) const VL_MT_SAFE { return m_propsp->right(dim); } @@ -236,8 +258,8 @@ protected: friend class VerilatedScope; // CONSTRUCTORS VerilatedVar(const char* namep, void* datap, VerilatedVarType vltype, - VerilatedVarFlags vlflags, int dims, bool isParam) - : VerilatedVarProps{vltype, vlflags, (dims > 0 ? 1 : 0), ((dims > 1) ? dims - 1 : 0)} + VerilatedVarFlags vlflags, int udims, int pdims, bool isParam) + : VerilatedVarProps{vltype, vlflags, udims, pdims} , m_datap{datap} , m_namep{namep} , m_isParam{isParam} {} @@ -246,8 +268,6 @@ public: ~VerilatedVar() = default; // ACCESSORS void* datap() const { return m_datap; } - const VerilatedRange& range() const { return packed(); } // Deprecated - const VerilatedRange& array() const { return unpacked(); } // Deprecated const char* name() const { return m_namep; } bool isParam() const { return m_isParam; } }; diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 795f367ae..7e661b4c6 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -85,7 +85,7 @@ public: // To simplify our free list, we use a size large enough for all derived types // We reserve word zero for the next pointer, as that's safer in case a // dangling reference to the original remains around. - static constexpr size_t CHUNK_SIZE = 96; + static constexpr size_t CHUNK_SIZE = 128; if (VL_UNCOVERABLE(size > CHUNK_SIZE)) VL_FATAL_MT(__FILE__, __LINE__, "", "increase CHUNK_SIZE"); if (VL_LIKELY(t_freeHeadp)) { @@ -171,9 +171,9 @@ protected: const VerilatedVar* m_varp = nullptr; const VerilatedScope* m_scopep = nullptr; std::string m_fullname; - const VerilatedRange& get_range() const { - // Determine number of dimensions and return outermost - return (m_varp->dims() > 1) ? m_varp->unpacked() : m_varp->packed(); + int32_t m_indexedDim = -1; + const VerilatedRange* get_range() const { + return m_varp->range(m_indexedDim + 1); } public: @@ -186,6 +186,7 @@ public: m_varp = varp->m_varp; m_scopep = varp->m_scopep; m_fullname = varp->m_fullname; + m_indexedDim = varp->m_indexedDim; } } static VerilatedVpioVarBase* castp(vpiHandle h) { @@ -193,10 +194,36 @@ public: } const VerilatedVar* varp() const { return m_varp; } const VerilatedScope* scopep() const { return m_scopep; } - uint32_t size() const override { return get_range().elements(); } - const VerilatedRange* rangep() const override { return &get_range(); } + // Returns the number of the currently indexed dimension (starting at -1 for none). + int32_t indexedDim() const { return m_indexedDim; } + // Returns whether the currently indexed dimension is unpacked. + bool isIndexedDimUnpacked() const { return indexedDim() + 1 < varp()->udims(); } + // Returns a maximum accessible dimension number, counting only unpacked dimensions + // (if onlyUnpacked == true), or both unpacked + packed. + int32_t maxDim(bool onlyUnpacked) const { + return onlyUnpacked ? varp()->udims() - 1 : varp()->dims() - 1; + } + // Returns a number of elements in the array, stopping at the unpacked-packed boundary. + uint32_t size() const override { + const int maxDimNum = maxDim(isIndexedDimUnpacked()); + int size = 1; + for (int dim = indexedDim() + 1; dim <= maxDimNum; dim++) + size *= varp()->range(dim)->elements(); + return size; + } + // If the array is unpacked, returns the bitsize of a single underlying packed element. + // If the array is packed, returns the bitsize of the whole array. + uint32_t bitSize() const { + if (isIndexedDimUnpacked()) + return varp()->entBits(); + else + return size(); + } + const VerilatedRange* rangep() const override { return get_range(); } const char* name() const override { return m_varp->name(); } const char* fullname() const override { return m_fullname.c_str(); } + virtual void* varDatap() const { return m_varp->datap(); } + virtual uint32_t bitOffset() const { return 0; } }; class VerilatedVpioParam final : public VerilatedVpioVarBase { @@ -221,7 +248,6 @@ public: default: return vpiUndefined; } } - void* varDatap() const { return m_varp->datap(); } }; class VerilatedVpioRange final : public VerilatedVpio { @@ -240,25 +266,27 @@ public: }; class VerilatedVpioRangeIter final : public VerilatedVpio { - // Only supports 1 dimension - const VerilatedRange* const m_rangep; - bool m_done = false; + const std::vector m_ranges; + std::vector::const_iterator m_iter; public: - explicit VerilatedVpioRangeIter(const VerilatedRange* rangep) - : m_rangep{rangep} {} + explicit VerilatedVpioRangeIter(const std::vector& ranges) + : m_ranges{ranges} { + m_iter = m_ranges.begin(); + } ~VerilatedVpioRangeIter() override = default; static VerilatedVpioRangeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { + if (VL_UNLIKELY(m_iter == m_ranges.end())) { delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle return nullptr; } - m_done = true; - return ((new VerilatedVpioRange{m_rangep})->castVpiHandle()); + VerilatedRange* const rangep = new VerilatedRange(*m_iter); + ++m_iter; + return ((new VerilatedVpioRange{rangep})->castVpiHandle()); } }; @@ -297,14 +325,15 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase { uint32_t u32; } m_mask; // memoized variable mask uint32_t m_entSize = 0; // memoized variable size + uint32_t m_bitOffset = 0; protected: void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries - int32_t m_index = 0; + std::vector m_index; public: VerilatedVpioVar(const VerilatedVar* varp, const VerilatedScope* scopep) : VerilatedVpioVarBase{varp, scopep} { - m_mask.u32 = VL_MASK_I(varp->packed().elements()); + m_mask.u32 = VL_MASK_I(varp->entBits()); m_entSize = varp->entSize(); m_varDatap = varp->datap(); } @@ -326,10 +355,30 @@ public: static VerilatedVpioVar* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); } + uint32_t bitOffset() const override { return m_bitOffset; } uint32_t mask() const { return m_mask.u32; } uint8_t mask_byte(int idx) const { return m_mask.u8[idx & 3]; } uint32_t entSize() const { return m_entSize; } - uint32_t index() const { return m_index; } + const std::vector& index() const { return m_index; } + VerilatedVpioVar* withIndex(int32_t index) const { + if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) + return nullptr; + + auto ret = new VerilatedVpioVar{this}; + ret->m_index.push_back(index); + ret->m_indexedDim++; + + int chunkSize = 1; + for (int dim = maxDim(isIndexedDimUnpacked()); dim > indexedDim() + 1; dim--) + chunkSize *= varp()->range(dim)->elements(); + + if (isIndexedDimUnpacked()) + ret->m_varDatap = (static_cast(ret->m_varDatap)) + entSize() * chunkSize * (index - get_range()->low()); + else + ret->m_bitOffset += chunkSize * (index - get_range()->low()); + + return ret; + } uint32_t type() const override { uint32_t type = vpiReg; switch (varp()->vltype()) { @@ -337,10 +386,21 @@ public: case VLVT_STRING: type = vpiStringVar; break; default: break; } - return (varp()->dims() > 1) ? vpiMemory : type; // but might be wire, logic + if (isIndexedDimUnpacked()) + return vpiRegArray; + else + return type; + } + const char* fullname() const override { + static thread_local std::string t_out; + t_out = std::string{scopep()->name()} + "." + name(); + for (auto idx : index()) { + t_out += "[" + std::to_string(idx) + "]"; + } + return t_out.c_str(); } void* prevDatap() const { return m_prevDatap; } - void* varDatap() const { return m_varDatap; } + void* varDatap() const override { return m_varDatap; } void createPrevDatap() { if (VL_UNLIKELY(!m_prevDatap)) { m_prevDatap = new uint8_t[entSize()]; @@ -349,31 +409,6 @@ public: } }; -class VerilatedVpioMemoryWord final : public VerilatedVpioVar { -public: - VerilatedVpioMemoryWord(const VerilatedVar* varp, const VerilatedScope* scopep, int32_t index, - int offset) - : VerilatedVpioVar{varp, scopep} { - m_index = index; - m_varDatap = (static_cast(varp->datap())) + entSize() * offset; - } - ~VerilatedVpioMemoryWord() override = default; - static VerilatedVpioMemoryWord* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); - } - uint32_t type() const override { return vpiMemoryWord; } - uint32_t size() const override { return varp()->packed().elements(); } - const VerilatedRange* rangep() const override { return &(varp()->packed()); } - const char* fullname() const override { - static thread_local std::string t_out; - constexpr size_t LEN_MAX_INDEX = 25; - char num[LEN_MAX_INDEX]; - VL_SNPRINTF(num, LEN_MAX_INDEX, "%d", m_index); - t_out = std::string{scopep()->name()} + "." + name() + "[" + num + "]"; - return t_out.c_str(); - } -}; - class VerilatedVpioVarIter final : public VerilatedVpio { const VerilatedScope* const m_scopep; VerilatedVarNameMap::const_iterator m_it; @@ -433,35 +468,54 @@ public: } }; -class VerilatedVpioMemoryWordIter final : public VerilatedVpio { - const vpiHandle m_handle; - const VerilatedVar* const m_varp; - int32_t m_iteration; - const int32_t m_direction; - bool m_done = false; +class VerilatedVpioRegIter final : public VerilatedVpio { + VerilatedVpioVar* m_var; + std::vector m_ranges; + std::vector m_nextIndex; + const int32_t m_maxDim; public: - VerilatedVpioMemoryWordIter(const vpiHandle handle, const VerilatedVar* varp) - : m_handle{handle} - , m_varp{varp} - , m_iteration{varp->unpacked().right()} - , m_direction{VL_LIKELY(varp->unpacked().left() > varp->unpacked().right()) ? 1 : -1} {} - ~VerilatedVpioMemoryWordIter() override = default; - static VerilatedVpioMemoryWordIter* castp(vpiHandle h) { - return dynamic_cast(reinterpret_cast(h)); + explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop) + : m_var{new VerilatedVpioVar(vop)} + , m_maxDim{vop->varp()->udims() - 1} { + for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) + m_ranges.push_back(*vop->varp()->range(it)); + for (auto it : m_ranges) + m_nextIndex.push_back(it.right()); + } + ~VerilatedVpioRegIter() override = default; + static VerilatedVpioRegIter* castp(vpiHandle h) { + return dynamic_cast(reinterpret_cast(h)); } uint32_t type() const override { return vpiIterator; } - void iterationInc() { - if (!(m_done = (m_iteration == m_varp->unpacked().left()))) m_iteration += m_direction; - } vpiHandle dovpi_scan() override { - if (VL_UNLIKELY(m_done)) { - delete this; // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle + if (VL_UNLIKELY(m_var->indexedDim() >= m_maxDim)) { + // Trying to iterate over a non-array object + delete this; return nullptr; } - const vpiHandle result = vpi_handle_by_index(m_handle, m_iteration); - iterationInc(); - return result; + if (m_nextIndex.front() > m_ranges.front().high() || + m_nextIndex.front() < m_ranges.front().low()) { + // Finished iterating + delete this; + return nullptr; + } + + VerilatedVpioVar* ret = m_var; + for (auto it : m_nextIndex) + ret = ret->withIndex(it); + + // Increase the index, pretending the dimensions are flattened + for (int32_t it = m_ranges.size() - 1; it >= 0; it--) { + m_nextIndex.at(it) += m_ranges.at(it).increment(); + if (m_nextIndex.at(it) <= m_ranges.at(it).high() && + m_nextIndex.at(it) >= m_ranges.at(it).low()) + break; + else if (it > 0) + m_nextIndex.at(it) = m_ranges.at(it).right(); + } + + return ret->castVpiHandle(); } }; @@ -686,7 +740,7 @@ public: break; } case VLVT_WDATA: { - words = VL_WORDS_I(vop->varp()->packed().elements()); + words = VL_WORDS_I(vop->varp()->entBits()); break; } default: break; @@ -2047,26 +2101,17 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_handle_by_index %p %d\n", object, indx);); VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); - // Memory words are not indexable - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); - if (VL_UNLIKELY(vop)) return nullptr; const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { - if (varop->varp()->dims() < 2) return nullptr; - if (VL_LIKELY(varop->varp()->unpacked().left() >= varop->varp()->unpacked().right())) { - if (VL_UNLIKELY(indx > varop->varp()->unpacked().left() - || indx < varop->varp()->unpacked().right())) - return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().right()}) - ->castVpiHandle(); - } - if (VL_UNLIKELY(indx < varop->varp()->unpacked().left() - || indx > varop->varp()->unpacked().right())) + // Case: no dimensions left to index + if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr; - return (new VerilatedVpioMemoryWord{varop->varp(), varop->scopep(), indx, - indx - varop->varp()->unpacked().left()}) - ->castVpiHandle(); + + // Case: index out of range + if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high())) + return nullptr; + + return varop->withIndex(indx)->castVpiHandle(); } VL_VPI_INTERNAL_(__FILE__, __LINE__, "%s : can't resolve handle", __func__); return nullptr; @@ -2108,7 +2153,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { case vpiIndex: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - const int32_t val = vop->index(); + const int32_t val = vop->index().back(); return (new VerilatedVpioConst{val})->castVpiHandle(); } case vpiScope: { @@ -2117,7 +2162,7 @@ vpiHandle vpi_handle(PLI_INT32 type, vpiHandle object) { return (new VerilatedVpioScope{vop->scopep()})->castVpiHandle(); } case vpiParent: { - const VerilatedVpioMemoryWord* const vop = VerilatedVpioMemoryWord::castp(object); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; return (new VerilatedVpioVar{vop->varp(), vop->scopep()})->castVpiHandle(); } @@ -2139,35 +2184,26 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { VerilatedVpiImp::assertOneCheck(); VL_VPI_ERROR_RESET_(); switch (type) { - case vpiMemoryWord: { - const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return (new VerilatedVpioMemoryWordIter{object, vop->varp()})->castVpiHandle(); - } case vpiRange: { const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); if (VL_UNLIKELY(!vop)) return nullptr; - if (vop->varp()->dims() < 2) return nullptr; - // Unsupported is multidim list - if (vop->varp()->dims() > 2) { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: %s, object %s has unsupported number of indices (%d)", __func__, - VerilatedVpiError::strFromVpiMethod(type), vop->fullname(), - vop->varp()->dims()); - } - return ((new VerilatedVpioRangeIter{vop->rangep()})->castVpiHandle()); + + std::vector ranges; + const int maxDim = vop->maxDim(vop->isIndexedDimUnpacked()); + for (int dim = vop->indexedDim() + 1; dim <= maxDim; dim++) + ranges.emplace_back(*vop->varp()->range(dim)); + + // allow one more range layer (regbit) + if (ranges.empty()) + ranges.emplace_back(VerilatedRange(0, 0)); + return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle()); } case vpiReg: { - const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); - if (VL_UNLIKELY(!vop)) return nullptr; - return ((new VerilatedVpioVarIter{vop})->castVpiHandle()); + const VerilatedVpioScope* const vscopep = VerilatedVpioScope::castp(object); + if (vscopep) return ((new VerilatedVpioVarIter{vscopep, false})->castVpiHandle()); + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (vop) return ((new VerilatedVpioRegIter{vop})->castVpiHandle()); + return nullptr; } case vpiParameter: { const VerilatedVpioScope* const vop = VerilatedVpioScope::castp(object); @@ -2249,7 +2285,7 @@ PLI_INT32 vpi_get(PLI_INT32 property, vpiHandle object) { case vpiVector: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); if (VL_UNLIKELY(!vop)) return vpiUndefined; - return (property == vpiVector) ^ (vop->varp()->dims() == 0); + return (property == vpiVector) ^ (vop->varp()->packedRanges().empty() || !vop->rangep()); } case vpiSize: { const VerilatedVpioVarBase* const vop = VerilatedVpioVarBase::castp(object); @@ -2347,7 +2383,9 @@ bool vl_check_format(const VerilatedVar* varp, const p_vpi_value valuep, const c switch (varp->vltype()) { case VLVT_UINT8: case VLVT_UINT16: - case VLVT_UINT32: return status; + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return status; default: status = false; } } else if (valuep->format == vpiRealVal) { @@ -2384,11 +2422,136 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) { va_end(args); } -void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, - const char* fullname) { +// Information about how to access packed array data. +// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word boundaries, +// in which case m_maskHi != 0. +template +struct VarAccessInfo final { + T* m_datap; // Typed pointer to packed array base address + size_t m_bitOffset; // Data start location (bit offset) + size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only) + T m_maskLo; // Access mask for m_datap[m_wordOffset] + T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only) +}; + +template +VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + // VarAccessInfo generation + // vop - variable to access (already indexed) + // bitCount - how many bits to write/read + // addOffset - additional offset to apply (within the packed array element) + + const size_t wordBits = sizeof(T) * 8; + uint32_t varBits = vop->bitSize(); + + if (vop->varp()->vltype() == VLVT_REAL) + varBits *= sizeof(double) * 8; + + // make sure we're not trying to write outside var bounds + assert(varBits > addOffset); + bitCount = std::min(bitCount, varBits - addOffset); + + VarAccessInfo info; + info.m_datap = reinterpret_cast(vop->varDatap()); + if (vop->varp()->vltype() == VLVT_WDATA) { + assert(sizeof(T) == sizeof(EData)); + assert(bitCount <= wordBits); + info.m_wordOffset = (vop->bitOffset() + addOffset) / wordBits; + info.m_bitOffset = (vop->bitOffset() + addOffset) % wordBits; + if (bitCount + info.m_bitOffset <= wordBits) { + // within single word + if (bitCount == wordBits) + info.m_maskLo = ~static_cast(0); + else + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = 0; + } else { + // straddles word boundary + info.m_maskLo = (static_cast(1) << (wordBits - info.m_bitOffset)) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + info.m_maskHi = (static_cast(1) << (bitCount + info.m_bitOffset - wordBits)) - 1; + } + } else { + info.m_wordOffset = 0; + info.m_bitOffset = vop->bitOffset() + addOffset; + assert(bitCount + info.m_bitOffset <= wordBits); + if (bitCount < wordBits) { + info.m_maskLo = (static_cast(1) << bitCount) - 1; + info.m_maskLo = info.m_maskLo << info.m_bitOffset; + } else { + info.m_maskLo = ~static_cast(0); + } + info.m_maskHi = 0; + } + return info; +} + +template +T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + if (info.m_maskHi) + return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset) | + ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi) << (wordBits - info.m_bitOffset)); + else + return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset; +} + +template +void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { + const size_t wordBits = sizeof(T) * 8; + const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); + + if (info.m_maskHi) { + info.m_datap[info.m_wordOffset + 1] = (info.m_datap[info.m_wordOffset+1] & ~info.m_maskHi) | + ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); + } + info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) | + ((word << info.m_bitOffset) & info.m_maskLo); +} + +// bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds +// addOffset: additional read bitoffset +QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); + return 0; + } +} + +// word: data to be written +// bitCount: maximum number of bits to write, will stop earlier if it reaches the var bounds +// addOffset: additional write bitoffset +void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) { + switch (vop->varp()->vltype()) { + case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); + } +} + +void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { + const VerilatedVar* varp = vop->varp(); + void* varDatap = vop->varDatap(); + const char* fullname = vop->fullname(); + if (!vl_check_format(varp, valuep, fullname, true)) return; // string data type is dynamic and may vary in size during simulation static thread_local std::string t_outDynamicStr; + + const int varBits = vop->bitSize(); + // We used to presume vpiValue.format = vpiIntVal or if single bit vpiScalarVal // This may cause backward compatibility issues with older code. if (valuep->format == vpiVectorVal) { @@ -2396,27 +2559,8 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, // It only needs to persist until the next vpi_get_value static thread_local t_vpi_vecval t_out[VL_VALUE_STRING_MAX_WORDS * 2]; valuep->value.vector = t_out; - if (varp->vltype() == VLVT_UINT8) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT16) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT32) { - t_out[0].aval = *(reinterpret_cast(varDatap)); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_UINT64) { - const QData data = *(reinterpret_cast(varDatap)); - t_out[1].aval = static_cast(data >> 32ULL); - t_out[1].bval = 0; - t_out[0].aval = static_cast(data); - t_out[0].bval = 0; - return; - } else if (varp->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(varp->packed().elements()); + if (varp->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); if (VL_UNCOVERABLE(words >= VL_VALUE_STRING_MAX_WORDS)) { VL_VPI_ERROR_( __FILE__, __LINE__, @@ -2424,82 +2568,63 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, "recompile"); return; } - const WDataInP datap = (reinterpret_cast(varDatap)); for (int i = 0; i < words; ++i) { - t_out[i].aval = datap[i]; + t_out[i].aval = vl_vpi_get_word(vop, 32, i * 32); t_out[i].bval = 0; } return; + } else if (varp->vltype() == VLVT_UINT64 && varBits > 32) { + const QData data = vl_vpi_get_word(vop, 64, 0); + t_out[1].aval = static_cast(data >> 32ULL); + t_out[1].bval = 0; + t_out[0].aval = static_cast(data); + t_out[0].bval = 0; + return; + } else { + t_out[0].aval = vl_vpi_get_word(vop, 32, 0); + t_out[0].bval = 0; + return; } } else if (valuep->format == vpiBinStrVal) { - int bits = varp->packed().elements(); - t_outDynamicStr.resize(bits); + t_outDynamicStr.resize(varBits); const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bits; ++i) { - const char val = (datap[i >> 3] >> (i & 7)) & 1; - t_outDynamicStr[bits - i - 1] = val ? '1' : '0'; + for (size_t i = 0; i < varBits; ++i) { + const size_t pos = i + vop->bitOffset(); + const char val = (datap[pos >> 3] >> (pos & 7)) & 1; + t_outDynamicStr[varBits - i - 1] = val ? '1' : '0'; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiOctStrVal) { - int chars = (varp->packed().elements() + 2) / 3; + const int chars = (varBits + 2) / 3; t_outDynamicStr.resize(chars); - const int bytes = VL_BYTES_I(varp->packed().elements()); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - const div_t idx = div(i * 3, 8); - int val = datap[idx.quot]; - if ((idx.quot + 1) < bytes) { - // if the next byte is valid or that in - // for when the required 3 bits straddle adjacent bytes - val |= datap[idx.quot + 1] << 8; - } - // align so least significant 3 bits represent octal char - val >>= idx.rem; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 3 - const unsigned int rem = varp->packed().elements() % 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } - t_outDynamicStr[chars - i - 1] = '0' + (val & 7); + const char val = vl_vpi_get_word(vop, 3, i * 3); + t_outDynamicStr[chars - i - 1] = '0' + val; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiDecStrVal) { if (varp->vltype() == VLVT_UINT8) { vl_strprintf(t_outDynamicStr, "%hhu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 8, 0))); } else if (varp->vltype() == VLVT_UINT16) { vl_strprintf(t_outDynamicStr, "%hu", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 16, 0))); } else if (varp->vltype() == VLVT_UINT32) { vl_strprintf(t_outDynamicStr, "%u", - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 32, 0))); } else if (varp->vltype() == VLVT_UINT64) { vl_strprintf(t_outDynamicStr, "%llu", // lintok-format-ll - static_cast(*(reinterpret_cast(varDatap)))); + static_cast(vl_vpi_get_word(vop, 64, 0))); } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } else if (valuep->format == vpiHexStrVal) { - int chars = (varp->packed().elements() + 3) >> 2; + const int chars = (varBits + 3) >> 2; t_outDynamicStr.resize(chars); - const CData* datap = (reinterpret_cast(varDatap)); for (size_t i = 0; i < chars; ++i) { - char val = (datap[i >> 1] >> ((i & 1) << 2)) & 15; - if (i == (chars - 1)) { - // most significant char, mask off nonexistent bits when vector - // size is not a multiple of 4 - const unsigned int rem = varp->packed().elements() & 3; - if (rem) { - // generate bit mask & zero nonexistent bits - val &= (1 << rem) - 1; - } - } + const char val = vl_vpi_get_word(vop, 4, i * 4); t_outDynamicStr[chars - i - 1] = "0123456789abcdef"[static_cast(val)]; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); @@ -2515,28 +2640,19 @@ void vl_get_value(const VerilatedVar* varp, void* varDatap, p_vpi_value valuep, return; } } else { - int bytes = VL_BYTES_I(varp->packed().elements()); - t_outDynamicStr.resize(bytes); - const CData* datap = (reinterpret_cast(varDatap)); - for (size_t i = 0; i < bytes; ++i) { - const char val = datap[bytes - i - 1]; + const int chars = VL_BYTES_I(varBits); + t_outDynamicStr.resize(chars); + for (size_t i = 0; i < chars; ++i) { + const char val = vl_vpi_get_word(vop, 8, i * 8); // other simulators replace [leading?] zero chars with spaces, replicate here. - t_outDynamicStr[i] = val ? val : ' '; + t_outDynamicStr[chars - i - 1] = val ? val : ' '; } valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } } else if (valuep->format == vpiIntVal) { - if (varp->vltype() == VLVT_UINT8) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT16) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } else if (varp->vltype() == VLVT_UINT32) { - valuep->value.integer = *(reinterpret_cast(varDatap)); - return; - } + valuep->value.integer = vl_vpi_get_word(vop, 32, 0); + return; } else if (valuep->format == vpiRealVal) { valuep->value.real = *(reinterpret_cast(varDatap)); return; @@ -2554,10 +2670,10 @@ void vpi_get_value(vpiHandle object, p_vpi_value valuep) { if (VL_UNLIKELY(!valuep)) return; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioParam* const vop = VerilatedVpioParam::castp(object)) { - vl_get_value(vop->varp(), vop->varDatap(), valuep, vop->fullname()); + vl_vpi_get_value(vop, valuep); return; } else if (const VerilatedVpioConst* const vop = VerilatedVpioConst::castp(object)) { if (valuep->format == vpiIntVal) { @@ -2580,7 +2696,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ VL_VPI_WARNING_(__FILE__, __LINE__, "Ignoring vpi_put_value with nullptr value pointer"); return nullptr; } - PLI_INT32 delay_mode = flags & 0xfff; + const PLI_INT32 delay_mode = flags & 0xfff; if (const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object)) { VL_DEBUG_IF_PLI( VL_DBG_MSGF("- vpi: vpi_put_value name=%s fmt=%d vali=%d\n", vop->fullname(), @@ -2607,97 +2723,51 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } VerilatedVpiImp::evalNeeded(true); + const int varBits = vop->bitSize(); if (valuep->format == vpiVectorVal) { if (VL_UNLIKELY(!valuep->value.vector)) return nullptr; - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + if (vop->varp()->vltype() == VLVT_WDATA) { + const int words = VL_WORDS_I(varBits); + for (int i = 0; i < words; ++i) + vl_vpi_put_word(vop, valuep->value.vector[i].aval, 32, i * 32); return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); + } else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) { + const QData val = (static_cast(valuep->value.vector[1].aval) << 32) | + static_cast(valuep->value.vector[0].aval); + vl_vpi_put_word(vop, val, 64, 0); return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) - = valuep->value.vector[0].aval & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = VL_SET_QII( - valuep->value.vector[1].aval & vop->mask(), valuep->value.vector[0].aval); - return object; - } else if (vop->varp()->vltype() == VLVT_WDATA) { - const int words = VL_WORDS_I(vop->varp()->packed().elements()); - WDataOutP datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < words; ++i) { - datap[i] = valuep->value.vector[i].aval; - if (i == (words - 1)) datap[i] &= vop->mask(); - } + } else { + vl_vpi_put_word(vop, valuep->value.vector[0].aval, 32, 0); return object; } } else if (valuep->format == vpiBinStrVal) { - const int bits = vop->varp()->packed().elements(); const int len = std::strlen(valuep->value.str); CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bits; ++i) { - const char set = (i < len) ? (valuep->value.str[len - i - 1] == '1') : 0; - // zero bits 7:1 of byte when assigning to bit 0, else - // or in 1 if bit set - if (i & 7) { - datap[i >> 3] |= set << (i & 7); - } else { - datap[i >> 3] = set; - } + for (int i = 0; i < varBits; ++i) { + const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); + const size_t pos = vop->bitOffset() + i; + + if (set) + datap[pos >> 3] |= 1 << (pos & 7); + else + datap[pos >> 3] &= ~(1 << (pos & 7)); } return object; } else if (valuep->format == vpiOctStrVal) { - const int chars = (vop->varp()->packed().elements() + 2) / 3; - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + const int chars = (varBits + 2) / 3; const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - div_t idx; - datap[0] = 0; // reset zero'th byte - for (int i = 0; i < chars; ++i) { - union { - char byte[2]; - uint16_t half; - } val; - idx = div(i * 3, 8); - if (i < len) { - // ignore illegal chars - const char digit = valuep->value.str[len - i - 1]; - if (digit >= '0' && digit <= '7') { - val.half = digit - '0'; - } else { - VL_VPI_WARNING_(__FILE__, __LINE__, - "%s: Non octal character '%c' in '%s' as value %s for %s", - __func__, digit, valuep->value.str, - VerilatedVpiError::strFromVpiVal(valuep->format), - vop->fullname()); - val.half = 0; - } - } else { - val.half = 0; - } - // align octal character to position within vector, note that - // the three bits may straddle a byte boundary so two byte wide - // assignments are made to adjacent bytes - but not if the least - // significant byte of the aligned value is the most significant - // byte of the destination. - val.half <<= idx.rem; - datap[idx.quot] |= val.byte[0]; // or in value - if ((idx.quot + 1) < bytes) { - datap[idx.quot + 1] = val.byte[1]; // this also resets - // all bits to 0 prior to or'ing above + for (int i = 0; i < len; ++i) { + char digit = valuep->value.str[len - i - 1] - '0'; + if (digit < 0 || digit > 7) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "%s: Non octal character '%c' in '%s' as value %s for %s", + __func__, digit + '0', valuep->value.str, + VerilatedVpiError::strFromVpiVal(valuep->format), + vop->fullname()); + digit = 0; } + vl_vpi_put_word(vop, digit, 3, i * 3); } - // mask off non-existent bits in the most significant byte - if (idx.quot == (bytes - 1)) { - datap[idx.quot] &= vop->mask_byte(idx.quot); - } else if (idx.quot + 1 == (bytes - 1)) { - datap[idx.quot + 1] &= vop->mask_byte(idx.quot + 1); - } - // zero off remaining top bytes - for (int i = idx.quot + 2; i < bytes; ++i) datap[i] = 0; return object; } else if (valuep->format == vpiDecStrVal) { char remainder[16]; @@ -2716,23 +2786,10 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ remainder, valuep->value.str, VerilatedVpiError::strFromVpiVal(valuep->format), vop->fullname()); } - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = val & vop->mask(); - return object; - } else if (vop->varp()->vltype() == VLVT_UINT64) { - *(reinterpret_cast(vop->varDatap())) = val; - (reinterpret_cast(vop->varDatap()))[1] &= vop->mask(); - return object; - } + vl_vpi_put_word(vop, val, 64, 0); + return object; } else if (valuep->format == vpiHexStrVal) { - const int chars = (vop->varp()->packed().elements() + 3) >> 2; - CData* const datap = (reinterpret_cast(vop->varDatap())); + const int chars = (varBits + 3) >> 2; const char* val = valuep->value.str; // skip hex ident if one is detected at the start of the string if (val[0] == '0' && (val[1] == 'x' || val[1] == 'X')) val += 2; @@ -2760,41 +2817,26 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ hex = 0; } // assign hex digit value to destination - if (i & 1) { - datap[i >> 1] |= hex << 4; - } else { - datap[i >> 1] = hex; // this also resets all - // bits to 0 prior to or'ing above of the msb - } + vl_vpi_put_word(vop, hex, 4, i * 4); } - // apply bit mask to most significant byte - datap[(chars - 1) >> 1] &= vop->mask_byte((chars - 1) >> 1); return object; } else if (valuep->format == vpiStringVal) { if (vop->varp()->vltype() == VLVT_STRING) { *(reinterpret_cast(vop->varDatap())) = valuep->value.str; return object; } else { - const int bytes = VL_BYTES_I(vop->varp()->packed().elements()); + const int chars = VL_BYTES_I(varBits); const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); - for (int i = 0; i < bytes; ++i) { + for (int i = 0; i < chars; ++i) { // prepend with 0 values before placing string the least significant bytes - datap[i] = (i < len) ? valuep->value.str[len - i - 1] : 0; + const char c = (i < len) ? valuep->value.str[len - i - 1] : 0; + vl_vpi_put_word(vop, c, 8, i * 8); } } return object; } else if (valuep->format == vpiIntVal) { - if (vop->varp()->vltype() == VLVT_UINT8) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT16) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } else if (vop->varp()->vltype() == VLVT_UINT32) { - *(reinterpret_cast(vop->varDatap())) = vop->mask() & valuep->value.integer; - return object; - } + vl_vpi_put_word(vop, valuep->value.integer, 64, 0); + return object; } else if (valuep->format == vpiRealVal) { if (vop->varp()->vltype() == VLVT_REAL) { *(reinterpret_cast(vop->varDatap())) = valuep->value.real; diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index b792de2b0..f8b4a6f9b 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -570,15 +570,28 @@ string AstVar::vlEnumDir() const { string AstVar::vlPropDecl(const string& propName) const { string out; + std::vector plims; // Packed dimension limits std::vector ulims; // Unpacked dimension limits - for (const AstNodeDType* dtp = dtypep(); dtp;) { - dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node - if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) { - ulims.push_back(adtypep->declRange().left()); - ulims.push_back(adtypep->declRange().right()); - dtp = adtypep->subDTypep(); - } else { - break; // AstBasicDType - nothing below + + if (const AstBasicDType* const bdtypep = basicp()) { + for (const AstNodeDType* dtp = dtypep(); dtp;) { + dtp = dtp->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node + if (const AstNodeArrayDType* const adtypep = VN_CAST(dtp, NodeArrayDType)) { + if (VN_IS(dtp, PackArrayDType)) { + plims.push_back(adtypep->declRange().left()); + plims.push_back(adtypep->declRange().right()); + } else { + ulims.push_back(adtypep->declRange().left()); + ulims.push_back(adtypep->declRange().right()); + } + dtp = adtypep->subDTypep(); + } else { + if (bdtypep->isRanged()) { + plims.push_back(bdtypep->left()); + plims.push_back(bdtypep->right()); + } + break; // AstBasicDType - nothing below + } } } @@ -595,23 +608,37 @@ string AstVar::vlPropDecl(const string& propName) const { out += "};\n"; } + if (!plims.empty()) { + out += "static const int " + propName + "__plims["; + out += cvtToStr(plims.size()); + out += "] = {"; + auto it = plims.cbegin(); + out += cvtToStr(*it); + while (++it != plims.cend()) { + out += ", "; + out += cvtToStr(*it); + } + out += "};\n"; + } + out += "static const VerilatedVarProps "; out += propName; out += "("; out += vlEnumType(); // VLVT_UINT32 etc out += ", " + vlEnumDir(); // VLVD_IN etc - if (const AstBasicDType* const bdtypep = basicp()) { - out += ", VerilatedVarProps::Packed()"; - out += ", " + cvtToStr(bdtypep->left()); - out += ", " + cvtToStr(bdtypep->right()); - } if (!ulims.empty()) { - out += ", VerilatedVarProps::Unpacked()"; + out += ", VerilatedVarProps::Unpacked{}"; out += ", " + cvtToStr(ulims.size() / 2); out += ", " + propName + "__ulims"; } + if (!plims.empty()) { + out += ", VerilatedVarProps::Packed{}"; + out += ", " + cvtToStr(plims.size() / 2); + out += ", " + propName + "__plims"; + } + out += ");\n"; return out; } diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 47b890ae8..b0ea5f576 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -929,21 +929,11 @@ void EmitCSyms::emitSymImp() { checkSplit(true); AstScope* const scopep = it->second.m_scopep; AstVar* const varp = it->second.m_varp; - // - int pwidth = 1; int pdim = 0; int udim = 0; string bounds; if (AstBasicDType* const basicp = varp->basicp()) { // Range is always first, it's not in "C" order - if (basicp->isRanged()) { - bounds += " ,"; - bounds += cvtToStr(basicp->hi()); - bounds += ","; - bounds += cvtToStr(basicp->lo()); - pdim++; - pwidth *= basicp->elements(); - } for (AstNodeDType* dtypep = varp->dtypep(); dtypep;) { dtypep = dtypep->skipRefp(); // Skip AstRefDType/AstTypedef, or return same node @@ -952,28 +942,25 @@ void EmitCSyms::emitSymImp() { bounds += cvtToStr(adtypep->left()); bounds += ","; bounds += cvtToStr(adtypep->right()); - if (VN_IS(dtypep, PackArrayDType)) { + if (VN_IS(dtypep, PackArrayDType)) pdim++; - pwidth *= adtypep->elementsConst(); - } else { + else udim++; - } dtypep = adtypep->subDTypep(); } else { + if (basicp->isRanged()) { + bounds += " ,"; + bounds += cvtToStr(basicp->hi()); + bounds += ","; + bounds += cvtToStr(basicp->lo()); + pdim++; + } break; // AstBasicDType - nothing below, 1 } } } - // TODO: actually expose packed arrays as vpiRegArray - if (pdim > 1 && udim == 0) { - bounds = ", "; - bounds += cvtToStr(pwidth - 1); - bounds += ",0"; - pdim = 1; - } - if (pdim > 1 || udim > 1) { - puts("//UNSUP "); // VerilatedImp can't deal with >2d or packed arrays - } + + putns(scopep, protect("__Vscope_" + it->second.m_scopeName)); putns(varp, ".varInsert(__Vfinal,"); putsQuoted(protect(it->second.m_varBasePretty)); @@ -1005,7 +992,9 @@ void EmitCSyms::emitSymImp() { puts(","); puts(varp->vlEnumDir()); // VLVD_IN etc puts(","); - puts(cvtToStr(pdim + udim)); + puts(cvtToStr(udim)); + puts(","); + puts(cvtToStr(pdim)); puts(bounds); puts(");\n"); ++m_numStmts; diff --git a/test_regress/t/t_scope_map.cpp b/test_regress/t/t_scope_map.cpp index 93de549dc..df6e22e1a 100644 --- a/test_regress/t/t_scope_map.cpp +++ b/test_regress/t/t_scope_map.cpp @@ -50,8 +50,8 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); - int varRight = varp->packed().right(); + int varLeft = varp->range(0)->left(); + int varRight = varp->range(0)->right(); #ifdef TEST_VERBOSE VL_PRINTF("\tVar = %s\n", varname.first); @@ -125,7 +125,7 @@ int main(int argc, char** argv) { for (const auto& varname : *varNameMap) { const VerilatedVar* varp = &(varname.second); - int varLeft = varp->packed().left(); + int varLeft = varp->range(0)->left(); int varBits = varLeft + 1; uint8_t* varData = reinterpret_cast(varp->datap()); diff --git a/test_regress/t/t_vpi_dump.out b/test_regress/t/t_vpi_dump.out index a100566d1..bf2aa75bf 100644 --- a/test_regress/t/t_vpi_dump.out +++ b/test_regress/t/t_vpi_dump.out @@ -7,18 +7,18 @@ t (vpiModule) t count (vpiReg) t.count do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst - fourthreetwoone (vpiMemory) t.fourthreetwoone - vpiMemoryWord: - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[3] - fourthreetwoone (vpiMemoryWord) t.fourthreetwoone[4] + fourthreetwoone (vpiRegArray) t.fourthreetwoone + vpiReg: + fourthreetwoone (vpiReg) t.fourthreetwoone[3] + fourthreetwoone (vpiReg) t.fourthreetwoone[4] half_count (vpiReg) t.half_count long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst onebit (vpiReg) t.onebit - quads (vpiMemory) t.quads - vpiMemoryWord: - quads (vpiMemoryWord) t.quads[3] - quads (vpiMemoryWord) t.quads[2] + quads (vpiRegArray) t.quads + vpiReg: + quads (vpiReg) t.quads[3] + quads (vpiReg) t.quads[2] real1 (vpiRealVar) t.real1 status (vpiReg) t.status str1 (vpiStringVar) t.str1 diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 021d761b6..8e2babd4f 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -49,7 +49,7 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int CHECK_RESULT(vpisize, size); // icarus verilog does not support vpiScalar, vpiVector or vpi*Range - if (TestSimulator::has_get_scalar()) { + if (TestSimulator::has_get_scalar() && type != vpiParameter) { int vpiscalar = vpi_get(vpiScalar, handle); CHECK_RESULT((bool)vpiscalar, (bool)scalar); int vpivector = vpi_get(vpiVector, handle); @@ -57,23 +57,29 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int } // Icarus only supports ranges on memories - if (!scalar && !(TestSimulator::is_icarus() && type != vpiMemory)) { - TestVpiHandle left_h, right_h; - + if (!scalar && type != vpiIntVar && type != vpiParameter + && !(TestSimulator::is_icarus() && type != vpiMemory)) { // check coherency for vectors - // get left hand side of range - left_h = vpi_handle(vpiLeftRange, handle); - CHECK_RESULT_NZ(left_h); - vpi_get_value(left_h, &value); - int coherency = value.value.integer; - // get right hand side of range - right_h = vpi_handle(vpiRightRange, handle); - CHECK_RESULT_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_MSG("%d:%d\n", coherency, value.value.integer); - coherency -= value.value.integer; - // calculate size & check - coherency = abs(coherency) + 1; + + int coherency = 1; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + CHECK_RESULT_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + CHECK_RESULT_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); + CHECK_RESULT(coherency, size); } @@ -83,12 +89,12 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int int vpidir = vpi_get(vpiDirection, handle); // Don't check port directions in verilator // See issue #681 - if (!TestSimulator::is_verilator()) CHECK_RESULT(vpidir, direction); + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa()) CHECK_RESULT(vpidir, direction); } // check type of object int vpitype = vpi_get(vpiType, handle); - if (!(TestSimulator::is_verilator() && type == vpiPort)) { + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa() && type == vpiPort) { // Don't check for ports in verilator // See issue #681 CHECK_RESULT(vpitype, type); @@ -117,13 +123,11 @@ int mon_check_props() { static struct params values[] = {{"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"onetwo", - {2, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiMemory}, - {0, 0, 0, 0}}, + {"onetwo", {2, vpiNoDirection, 1, vpiRegArray}, {0, 0, 0, 0}}, {"fourthreetwoone", - {2, vpiNoDirection, 0, vpiMemory}, - {2, vpiNoDirection, 0, vpiMemoryWord}}, - {"theint", {32, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {2, vpiNoDirection, 0, vpiRegArray}, + {2, vpiNoDirection, 0, vpiReg}}, + {"theint", {32, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiIntVar}, {0, 0, 0, 0}}, {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, @@ -145,7 +149,7 @@ int mon_check_props() { return status; if (value->children.size) { int size = 0; - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, h); while (TestVpiHandle word_h = vpi_scan(iter_h)) { // check size and range if (int status diff --git a/test_regress/t/t_vpi_memory.cpp b/test_regress/t/t_vpi_memory.cpp index e61cf737a..857ee2d90 100644 --- a/test_regress/t/t_vpi_memory.cpp +++ b/test_regress/t/t_vpi_memory.cpp @@ -52,25 +52,27 @@ void _mon_check_range(const TestVpiHandle& handle, int size, int left, int right int vpisize = vpi_get(vpiSize, handle); TEST_CHECK_EQ(vpisize, size); } - int coherency; + // check coherency + int coherency = 1; { - // check left hand side of range - TestVpiHandle left_h = vpi_handle(vpiLeftRange, handle); - TEST_CHECK_NZ(left_h); - vpi_get_value(left_h, &value); - TEST_CHECK_EQ(value.value.integer, left); - coherency = value.value.integer; + TestVpiHandle iter_h = vpi_iterate(vpiRange, handle); + while (TestVpiHandle range_h = vpi_scan(iter_h)) { + int rangeSize; + TestVpiHandle left_h, right_h; + // get left hand side of range + left_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + rangeSize = value.value.integer; + // get right hand side of range + right_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + rangeSize = abs(rangeSize - value.value.integer) + 1; + coherency *= rangeSize; + } + iter_h.freed(); } - { - // check right hand side of range - TestVpiHandle right_h = vpi_handle(vpiRightRange, handle); - TEST_CHECK_NZ(right_h); - vpi_get_value(right_h, &value); - TEST_CHECK_EQ(value.value.integer, right); - coherency -= value.value.integer; - } - // calculate size & check - coherency = abs(coherency) + 1; TEST_CHECK_EQ(coherency, size); } @@ -83,8 +85,8 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_NZ(mem_h); // check type int vpitype = vpi_get(vpiType, mem_h); - if (vpitype != vpiMemory && vpitype != vpiReg) { - printf("%%Error: %s:%d vpiType neither vpiMemory or vpiReg: %d\n", FILENM, __LINE__, + if (vpitype != vpiRegArray && vpitype != vpiReg) { + printf("%%Error: %s:%d vpiType neither vpiRegArray or vpiReg: %d\n", FILENM, __LINE__, vpitype); errors++; } @@ -96,9 +98,9 @@ void _mem_check(const char* name, int size, int left, int right, int words) { } } // iterate and store - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { _mon_check_range(mem_h, words, words, 1); - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { value.format = vpiIntVal; @@ -112,15 +114,15 @@ void _mem_check(const char* name, int size, int left, int right, int words) { TEST_CHECK_EQ(cnt, words); // should be words addresses } else { int expSize = size * words; - _mon_check_range(mem_h, expSize, expSize - 1, 0); + _mon_check_range(mem_h, expSize, words, 1); value.format = vpiBinStrVal; value.value.str = const_cast(binStr.c_str()); vpi_put_value(mem_h, &value, NULL, vpiNoDelay); TEST_CHECK_Z(vpi_chk_error(&e)); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // iterate and accumulate - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int cnt = 0; while (TestVpiHandle lcl_h = vpi_scan(iter_h)) { ++cnt; @@ -148,7 +150,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { { // make sure trying to get properties that don't exist // doesn't crash - TestVpiHandle iter_h = vpi_iterate(vpiMemoryWord, mem_h); + TestVpiHandle iter_h = vpi_iterate(vpiReg, mem_h); int should_be_undefined = vpi_get(vpiSize, iter_h); TEST_CHECK_EQ(should_be_undefined, vpiUndefined); should_be_undefined = vpi_get(vpiIndex, iter_h); @@ -160,7 +162,7 @@ void _mem_check(const char* name, int size, int left, int right, int words) { should_be_NULL = vpi_handle(vpiScope, iter_h); TEST_CHECK_EQ(should_be_NULL, 0); } - if (vpitype == vpiMemory) { + if (vpitype == vpiRegArray) { // check vpiRange TestVpiHandle iter_h = vpi_iterate(vpiRange, mem_h); TEST_CHECK_NZ(iter_h); diff --git a/test_regress/t/t_vpi_multidim.cpp b/test_regress/t/t_vpi_multidim.cpp new file mode 100644 index 000000000..f66c28dec --- /dev/null +++ b/test_regress/t/t_vpi_multidim.cpp @@ -0,0 +1,462 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// 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 +// +//************************************************************************* + +#ifdef IS_VPI + +#include "vpi_user.h" + +#include + +#else + +#include "verilated.h" +#include "verilated_vcd_c.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_multidim.h" +#include "Vt_vpi_multidim__Dpi.h" +#include "svdpi.h" + +#endif + +#include +#include +#include +#include +#include + +// These require the above. Comment prevents clang-format moving them +#include "TestCheck.h" +#include "TestSimulator.h" +#include "TestVpi.h" + +int errors = 0; + +// TEST START + +void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRangeHigh, int expRangeLow) +{ + const int vpitype = vpi_get(vpiType, arr_h); + TEST_CHECK_EQ(vpitype, expType); + const int vpisize = vpi_get(vpiSize, arr_h); + TEST_CHECK_EQ(vpisize, expSize); + + s_vpi_value value; + value.format = vpiIntVal; + + TestVpiHandle left_h = vpi_handle(vpiLeftRange, arr_h); + TEST_CHECK_NZ(left_h); + vpi_get_value(left_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeHigh); + + TestVpiHandle right_h = vpi_handle(vpiRightRange, arr_h); + TEST_CHECK_NZ(right_h); + vpi_get_value(right_h, &value); + TEST_CHECK_EQ(value.value.integer, expRangeLow); +} + +void _arr_iter_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + _arr_type_check(arr_h, vpiRegArray, 4, lows[0] + 1, lows[0]); + + { + // can't iterate through RegArrays on a nested RegArray + TestVpiHandle arr_iter_h = vpi_iterate(vpiRegArray, arr_h); + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + if (!TestSimulator::is_questa()) { + // but we can access them by index (Questa can't) + for (int idx = lows[0]; idx < lows[0] + 2; idx++) { + TestVpiHandle arr_elem_h = vpi_handle_by_index(arr_h, idx); + TEST_CHECK_NZ(arr_elem_h); + + // first indexing yields size-2 RegArrays + _arr_type_check(arr_elem_h, vpiRegArray, 2, lows[1] + 1, lows[1]); + + for (int idx2 = lows[1]; idx2 < lows[1] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // second indexing yields wordSize Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + } + } + } + + { + // it's also possible to directly iterate through all four Regs + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + for (int idx = 0; idx < 4; idx++) { + TestVpiHandle arr_elem_h = vpi_scan(arr_iter_h); + TEST_CHECK_NZ(arr_elem_h); + + // which gives us wordSize Regs + _arr_type_check(arr_elem_h, vpiReg, wordSize, lows[2] + 1, lows[2]); + + { + // can't iterate through Regs on a nested Reg + TestVpiHandle arr_iter2_h = vpi_iterate(vpiReg, arr_elem_h); + TEST_CHECK_Z(vpi_scan(arr_iter2_h)); + arr_iter2_h.freed(); + } + + // but we can access them by index + for (int idx2 = lows[2]; idx2 < lows[2] + 2; idx2++) { + TestVpiHandle arr_elem2_h = vpi_handle_by_index(arr_elem_h, idx2); + TEST_CHECK_NZ(arr_elem2_h); + + // first indexing yields wordSize / 2 Regs + _arr_type_check(arr_elem2_h, vpiReg, wordSize / 2, lows[3] + wordSize / 2 - 1, lows[3]); + + for (int idx3 = lows[3]; idx3 < lows[3] + wordSize / 2; idx3++) { + TestVpiHandle arr_elem3_h = vpi_handle_by_index(arr_elem2_h, idx3); + TEST_CHECK_NZ(arr_elem3_h); + { + // second indexing yields size-1 RegBits (no support for RegBit VPI type yet) + const int vpitype = vpi_get(vpiType, arr_elem3_h); + if (TestSimulator::is_verilator()) { + TEST_CHECK_EQ(vpitype, vpiReg); + } else { + TEST_CHECK_EQ(vpitype, vpiRegBit); + } + const int vpisize = vpi_get(vpiSize, arr_elem3_h); + TEST_CHECK_EQ(vpisize, 1); + } + } + } + + // iterating through packed ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_elem_h); + for (int idx2 = 0; idx2 < 2; idx2++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3] + wordSize / 2 - 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx2 == 0) { + TEST_CHECK_EQ(value.value.integer, lows[2]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[3]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } + TEST_CHECK_Z(vpi_scan(arr_iter_h)); + arr_iter_h.freed(); + } + + { + // iterating through unpacked ranges + TestVpiHandle range_iter_h = vpi_iterate(vpiRange, arr_h); + for (int idx = 0; idx < 2; idx++) { + TestVpiHandle range_h = vpi_scan(range_iter_h); + TEST_CHECK_NZ(range_h); + { + s_vpi_value value; + value.format = vpiIntVal; + TestVpiHandle side_h = vpi_handle(vpiLeftRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0] + 1); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1] + 1); + } + side_h = vpi_handle(vpiRightRange, range_h); + TEST_CHECK_NZ(side_h); + vpi_get_value(side_h, &value); + if (idx == 0) { + TEST_CHECK_EQ(value.value.integer, lows[0]); + } else { + TEST_CHECK_EQ(value.value.integer, lows[1]); + } + } + } + TEST_CHECK_Z(vpi_scan(range_iter_h)); + range_iter_h.freed(); + } +} + +void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* lows, const char *octVal_s, PLI_INT32 format) +{ + const int spanSize = wordSize / 2; + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + char zero_s[2] = "0"; + + // zero out the vector + value_in.format = vpiOctStrVal; + value_in.value.str = zero_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + value_in.format = format; + value_out.format = format; + + for (int i = 0; i < 2; i++) { + TestVpiHandle subreg_h = vpi_handle_by_index(reg_h, lows[2] + i); + TEST_CHECK_NZ(subreg_h); + + char octSpan_s[spanSize / 3 + 1]; + strncpy(octSpan_s, &octVal_s[spanSize / 3 * (1 - i)], spanSize / 3); + octSpan_s[spanSize / 3] = '\0'; + + uint64_t intVal; + t_vpi_vecval vecVal[2]; + sscanf(octSpan_s, "%" SCNo64, &intVal); + char strVal_s[spanSize + 1]; // max length of the string happens for binary + + if (format == vpiIntVal) { + value_in.value.integer = intVal; + } else if (format == vpiVectorVal) { + if (spanSize > 32) { + vecVal[1].aval = intVal >> 32; + vecVal[1].bval = 0; + } + vecVal[0].aval = intVal; + vecVal[0].bval = 0; + value_in.value.vector = vecVal; + } else if (format == vpiBinStrVal) { + for (int j = 0; j < spanSize; j++) + strVal_s[j] = (intVal >> (spanSize - j - 1)) % 2 + '0'; + strVal_s[spanSize] = '\0'; + value_in.value.str = strVal_s; + } else if (format == vpiDecStrVal) { + sprintf(strVal_s, "%" PRIu64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiHexStrVal) { + sprintf(strVal_s, "%0*" PRIx64, (spanSize + 3) / 4, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiOctStrVal) { + sprintf(strVal_s, "%" PRIo64, intVal); + value_in.value.str = strVal_s; + } else if (format == vpiStringVal) { + const int byteCount = (spanSize + 7) / 8; + for (int j = 0; j < byteCount; j++) + strVal_s[j] = (intVal >> (8 * (byteCount - j - 1))) & 0xff; + strVal_s[byteCount] = '\0'; + value_in.value.str = strVal_s; + } + + vpi_put_value(subreg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + + vpi_get_value(subreg_h, &value_out); + switch (format) { + case vpiIntVal: + TEST_CHECK_EQ(value_out.value.integer, value_in.value.integer); + break; + case vpiVectorVal: + if (spanSize > 32) + TEST_CHECK_EQ(value_out.value.vector[1].aval, value_in.value.vector[1].aval); + TEST_CHECK_EQ(value_out.value.vector[0].aval, value_in.value.vector[0].aval); + break; + case vpiStringVal: + TEST_CHECK_EQ(value_out.value.str[0], value_in.value.str[0] ? value_in.value.str[0] : ' '); + break; + case vpiBinStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiOctStrVal: + TEST_CHECK_CSTR(value_out.value.str, value_in.value.str); + break; + } + } + + // validate the resulting flattened vector + value_out.format = vpiOctStrVal; + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); +} + +std::default_random_engine rng; + +void _arr_access_check(const char* name, int wordSize, const int* lows) { + TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TEST_CHECK_NZ(arr_h); + + std::uniform_int_distribution rand64( + std::numeric_limits::min(), + std::numeric_limits::max() + ); + + char octVal_s[wordSize / 3 + 1]; + + // fill octVal_s with random octal digits + if (wordSize < 64) { + sprintf(octVal_s, "%0*" PRIo64, wordSize / 3, rand64(rng) % (static_cast(1) << wordSize)); + } else { + sprintf(octVal_s, "%0*" PRIo64, 63 / 3, rand64(rng)); + sprintf(octVal_s + 63 / 3, "%0*" PRIo64, (wordSize - 63) / 3, rand64(rng) % (static_cast(1) << (wordSize - 63))); + } + + // Assume that reading/writing to the "flattened" packed register is already tested, + // check only reading/writing to sub-regs and validate the flattened result. + { + TestVpiHandle arr_iter_h = vpi_iterate(vpiReg, arr_h); + while (TestVpiHandle reg_h = vpi_scan(arr_iter_h)) { + s_vpi_value value_in; + s_vpi_value value_out; + s_vpi_error_info e; + + value_out.format = vpiOctStrVal; + value_in.format = vpiOctStrVal; + value_in.value.str = octVal_s; + vpi_put_value(reg_h, &value_in, NULL, vpiNoDelay); + TEST_CHECK_Z(vpi_chk_error(&e)); + vpi_get_value(reg_h, &value_out); + TEST_CHECK_CSTR(value_out.value.str, octVal_s); + + // test each I/O data format + if (wordSize <= 64) { + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiIntVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiDecStrVal); + } + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiVectorVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiBinStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiOctStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiHexStrVal); + _arr_access_format_check(reg_h, wordSize, lows, octVal_s, vpiStringVal); + } + arr_iter_h.freed(); + } +} + +struct params { + const char* name; + int wordSize; + const int lows[4]; +}; + +void _multidim_check() { + static struct params values[] = { + {"arr_cdata", 6, {0, 1, 2, 3}}, + {"arr_sdata", 12, {4, 5, 6, 7}}, + {"arr_idata", 30, {8, 9, 10, 11}}, + {"arr_qdata", 60, {12, 13, 14, 15}}, + {"arr_wdata", 126, {16, 17, 18, 19}}, + {nullptr, 0, {0, 0, 0, 0}} + }; + struct params* value = values; + while (value->name) { + _arr_iter_check(value->name, value->wordSize, value->lows); + _arr_access_check(value->name, value->wordSize, value->lows); + value++; + } +} + +// TEST END + +extern "C" int mon_check() { + // Callback from initial block in monitor + //if (int status = _mon_check_param()) return status; + printf("-mon_check()\n"); + _multidim_check(); + return errors; +} + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + const std::unique_ptr contextp{new VerilatedContext}; + + uint64_t sim_time = 1100; // TODO + contextp->debug(0); + contextp->commandArgs(argc, argv); + + const std::unique_ptr topp{new VM_PREFIX{contextp.get(), + // Note null name - we're flattening it out + ""}}; + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + +#if VM_TRACE + contextp->traceEverOn(true); + VL_PRINTF("Enabling waves...\n"); + VerilatedVcdC* tfp = new VerilatedVcdC; + topp->trace(tfp, 99); + tfp->open(STRINGIFY(TEST_OBJ_DIR) "/simx.vcd"); +#endif + + topp->eval(); + topp->clk = 0; + contextp->timeInc(10); + + while (contextp->time() < sim_time && !contextp->gotFinish()) { + contextp->timeInc(1); + topp->eval(); + VerilatedVpi::callValueCbs(); + topp->clk = !topp->clk; + // mon_do(); +#if VM_TRACE + if (tfp) tfp->dump(main_time); +#endif + } + if (!contextp->gotFinish()) { + vl_fatal(__FILE__, __LINE__, "main", "%Error: Timeout; never got a $finish"); + } + topp->final(); + +#if VM_TRACE + if (tfp) tfp->close(); +#endif + + return 0; +} + +#endif diff --git a/test_regress/t/t_vpi_multidim.py b/test_regress/t/t_vpi_multidim.py new file mode 100755 index 000000000..ab0961ef9 --- /dev/null +++ b/test_regress/t/t_vpi_multidim.py @@ -0,0 +1,22 @@ +#!/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('simulator') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + v_flags2=["+define+USE_VPI_NOT_DPI"], + verilator_flags2=["--exe --vpi --no-l2name --public-flat-rw", test.pli_filename]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_multidim.v b/test_regress/t/t_vpi_multidim.v new file mode 100644 index 000000000..2cdd39c9b --- /dev/null +++ b/test_regress/t/t_vpi_multidim.v @@ -0,0 +1,44 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// 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 + +module t (/*AUTOARG*/ + // Inputs + clk + ); /*verilator public_module*/ + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + input clk; + + logic [3:2][5:3] arr_cdata [1:0][2:1]; // 2x3 (6) bit words + logic [7:6][12:7] arr_sdata [5:4][6:5]; // 2x6 (12) bit words + logic [11:10][25:11] arr_idata [9:8][10:9]; // 2x15 (30) bit words + logic [15:14][44:15] arr_qdata [13:12][14:13]; // 2x30 (60) bit words + logic [19:18][81:19] arr_wdata [17:16][18:17]; // 2x63 (126) bit words + + int status; + + initial begin +`ifdef VERILATOR + status = $c32("mon_check()"); +`else + status = $mon_check(); +`endif + if (status!=0) begin + $write("%%Error: t_vpi_multidim.cpp:%0d: C Test failed\n", status); + $stop; + end + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule : t diff --git a/test_regress/t/t_vpi_var.cpp b/test_regress/t/t_vpi_var.cpp index 99ab60f9f..2cb16563f 100644 --- a/test_regress/t/t_vpi_var.cpp +++ b/test_regress/t/t_vpi_var.cpp @@ -297,7 +297,7 @@ int _mon_check_var() { d = vpi_get(vpiVector, vh4); CHECK_RESULT(d, 1); p = vpi_get_str(vpiType, vh4); - CHECK_RESULT_CSTR(p, "vpiMemory"); + CHECK_RESULT_CSTR(p, "vpiRegArray"); } t_vpi_value tmpValue; @@ -320,14 +320,14 @@ int _mon_check_var() { CHECK_RESULT_CSTR(p, "vpiConstant"); } { - TestVpiHandle vh10 = vpi_iterate(vpiMemoryWord, vh4); + TestVpiHandle vh10 = vpi_iterate(vpiReg, vh4); CHECK_RESULT_NZ(vh10); p = vpi_get_str(vpiType, vh10); CHECK_RESULT_CSTR(p, "vpiIterator"); TestVpiHandle vh11 = vpi_scan(vh10); CHECK_RESULT_NZ(vh11); p = vpi_get_str(vpiType, vh11); - CHECK_RESULT_CSTR(p, "vpiMemoryWord"); + CHECK_RESULT_CSTR(p, "vpiReg"); TestVpiHandle vh12 = vpi_handle(vpiLeftRange, vh11); CHECK_RESULT_NZ(vh12); vpi_get_value(vh12, &tmpValue); @@ -643,15 +643,15 @@ int _mon_check_quad() { TestVpiHandle vhidx3 = vpi_handle_by_index(vh2, 3); CHECK_RESULT_NZ(vhidx3); - // Memory words should not be indexable + // Packed words should be indexable TestVpiHandle vhidx3idx0 = vpi_handle_by_index(vhidx3, 0); - CHECK_RESULT(vhidx3idx0, 0); + CHECK_RESULT_NZ(vhidx3idx0); TestVpiHandle vhidx2idx2 = vpi_handle_by_index(vhidx2, 2); - CHECK_RESULT(vhidx2idx2, 0); + CHECK_RESULT_NZ(vhidx2idx2); TestVpiHandle vhidx3idx3 = vpi_handle_by_index(vhidx3, 3); - CHECK_RESULT(vhidx3idx3, 0); + CHECK_RESULT_NZ(vhidx3idx3); TestVpiHandle vhidx2idx61 = vpi_handle_by_index(vhidx2, 61); - CHECK_RESULT(vhidx2idx61, 0); + CHECK_RESULT_NZ(vhidx2idx61); v.format = vpiVectorVal; v.value.vector = vv; From bb871728c92553c3813f556782807b5879135ea1 Mon Sep 17 00:00:00 2001 From: github action Date: Fri, 10 Jan 2025 00:05:16 +0000 Subject: [PATCH 28/93] Apply 'make format' --- include/verilated.cpp | 6 +- include/verilated_sym_props.h | 5 +- include/verilated_vpi.cpp | 123 +++++++++++++++--------------- src/V3EmitCSyms.cpp | 1 - test_regress/t/t_vpi_get.cpp | 41 +++++----- test_regress/t/t_vpi_multidim.cpp | 81 ++++++++++---------- 6 files changed, 126 insertions(+), 131 deletions(-) diff --git a/include/verilated.cpp b/include/verilated.cpp index ecf7e8d73..02333a810 100644 --- a/include/verilated.cpp +++ b/include/verilated.cpp @@ -3369,14 +3369,16 @@ void VerilatedScope::exportInsert(int finalize, const char* namep, void* cb) VL_ } void VerilatedScope::varInsert(int finalize, const char* namep, void* datap, bool isParam, - VerilatedVarType vltype, int vlflags, int udims, int pdims ...) VL_MT_UNSAFE { + VerilatedVarType vltype, int vlflags, int udims, + int pdims...) VL_MT_UNSAFE { // Grab dimensions // In the future we may just create a large table at emit time and // statically construct from that. if (!finalize) return; if (!m_varsp) m_varsp = new VerilatedVarNameMap; - VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, isParam); + VerilatedVar var(namep, datap, vltype, static_cast(vlflags), udims, pdims, + isParam); va_list ap; va_start(ap, pdims); diff --git a/include/verilated_sym_props.h b/include/verilated_sym_props.h index 0d7e5f4a2..53600e7a1 100644 --- a/include/verilated_sym_props.h +++ b/include/verilated_sym_props.h @@ -76,7 +76,7 @@ class VerilatedVarProps VL_NOT_FINAL { const VerilatedVarFlags m_vlflags; // Direction std::vector m_unpacked; // Unpacked array ranges std::vector m_packed; // Packed array ranges - VerilatedRange m_packedDpi; // Flattened packed array range + VerilatedRange m_packedDpi; // Flattened packed array range void initUnpacked(int udims, const int* ulims) { for (int i = 0; i < udims; ++i) { const int uleft = ulims ? ulims[2 * i + 0] : 0; @@ -152,8 +152,7 @@ public: uint32_t entSize() const VL_MT_SAFE; uint32_t entBits() const VL_MT_SAFE { uint32_t bits = 1; - for (auto it : m_packed) - bits *= it.elements(); + for (auto it : m_packed) bits *= it.elements(); return bits; } bool isPublicRW() const { return ((m_vlflags & VLVF_PUB_RW) != 0); } diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 7e661b4c6..53f953564 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -172,9 +172,7 @@ protected: const VerilatedScope* m_scopep = nullptr; std::string m_fullname; int32_t m_indexedDim = -1; - const VerilatedRange* get_range() const { - return m_varp->range(m_indexedDim + 1); - } + const VerilatedRange* get_range() const { return m_varp->range(m_indexedDim + 1); } public: VerilatedVpioVarBase(const VerilatedVar* varp, const VerilatedScope* scopep) @@ -272,8 +270,8 @@ class VerilatedVpioRangeIter final : public VerilatedVpio { public: explicit VerilatedVpioRangeIter(const std::vector& ranges) : m_ranges{ranges} { - m_iter = m_ranges.begin(); - } + m_iter = m_ranges.begin(); + } ~VerilatedVpioRangeIter() override = default; static VerilatedVpioRangeIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); @@ -326,6 +324,7 @@ class VerilatedVpioVar VL_NOT_FINAL : public VerilatedVpioVarBase { } m_mask; // memoized variable mask uint32_t m_entSize = 0; // memoized variable size uint32_t m_bitOffset = 0; + protected: void* m_varDatap = nullptr; // varp()->datap() adjusted for array entries std::vector m_index; @@ -361,8 +360,7 @@ public: uint32_t entSize() const { return m_entSize; } const std::vector& index() const { return m_index; } VerilatedVpioVar* withIndex(int32_t index) const { - if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) - return nullptr; + if (VL_UNLIKELY(indexedDim() + 1 >= varp()->dims())) return nullptr; auto ret = new VerilatedVpioVar{this}; ret->m_index.push_back(index); @@ -373,7 +371,8 @@ public: chunkSize *= varp()->range(dim)->elements(); if (isIndexedDimUnpacked()) - ret->m_varDatap = (static_cast(ret->m_varDatap)) + entSize() * chunkSize * (index - get_range()->low()); + ret->m_varDatap = (static_cast(ret->m_varDatap)) + + entSize() * chunkSize * (index - get_range()->low()); else ret->m_bitOffset += chunkSize * (index - get_range()->low()); @@ -394,9 +393,7 @@ public: const char* fullname() const override { static thread_local std::string t_out; t_out = std::string{scopep()->name()} + "." + name(); - for (auto idx : index()) { - t_out += "[" + std::to_string(idx) + "]"; - } + for (auto idx : index()) { t_out += "[" + std::to_string(idx) + "]"; } return t_out.c_str(); } void* prevDatap() const { return m_prevDatap; } @@ -478,11 +475,10 @@ public: explicit VerilatedVpioRegIter(const VerilatedVpioVar* vop) : m_var{new VerilatedVpioVar(vop)} , m_maxDim{vop->varp()->udims() - 1} { - for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) - m_ranges.push_back(*vop->varp()->range(it)); - for (auto it : m_ranges) - m_nextIndex.push_back(it.right()); - } + for (auto it = vop->indexedDim() + 1; it <= m_maxDim; it++) + m_ranges.push_back(*vop->varp()->range(it)); + for (auto it : m_ranges) m_nextIndex.push_back(it.right()); + } ~VerilatedVpioRegIter() override = default; static VerilatedVpioRegIter* castp(vpiHandle h) { return dynamic_cast(reinterpret_cast(h)); @@ -494,22 +490,21 @@ public: delete this; return nullptr; } - if (m_nextIndex.front() > m_ranges.front().high() || - m_nextIndex.front() < m_ranges.front().low()) { + if (m_nextIndex.front() > m_ranges.front().high() + || m_nextIndex.front() < m_ranges.front().low()) { // Finished iterating delete this; return nullptr; } VerilatedVpioVar* ret = m_var; - for (auto it : m_nextIndex) - ret = ret->withIndex(it); + for (auto it : m_nextIndex) ret = ret->withIndex(it); // Increase the index, pretending the dimensions are flattened for (int32_t it = m_ranges.size() - 1; it >= 0; it--) { m_nextIndex.at(it) += m_ranges.at(it).increment(); - if (m_nextIndex.at(it) <= m_ranges.at(it).high() && - m_nextIndex.at(it) >= m_ranges.at(it).low()) + if (m_nextIndex.at(it) <= m_ranges.at(it).high() + && m_nextIndex.at(it) >= m_ranges.at(it).low()) break; else if (it > 0) m_nextIndex.at(it) = m_ranges.at(it).right(); @@ -2104,8 +2099,7 @@ vpiHandle vpi_handle_by_index(vpiHandle object, PLI_INT32 indx) { const VerilatedVpioVar* const varop = VerilatedVpioVar::castp(object); if (VL_LIKELY(varop)) { // Case: no dimensions left to index - if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) - return nullptr; + if (VL_UNLIKELY(varop->indexedDim() + 1 > varop->varp()->dims() - 1)) return nullptr; // Case: index out of range if (VL_UNLIKELY(indx < varop->rangep()->low() || indx > varop->rangep()->high())) @@ -2194,8 +2188,7 @@ vpiHandle vpi_iterate(PLI_INT32 type, vpiHandle object) { ranges.emplace_back(*vop->varp()->range(dim)); // allow one more range layer (regbit) - if (ranges.empty()) - ranges.emplace_back(VerilatedRange(0, 0)); + if (ranges.empty()) ranges.emplace_back(VerilatedRange(0, 0)); return ((new VerilatedVpioRangeIter{ranges})->castVpiHandle()); } case vpiReg: { @@ -2423,19 +2416,20 @@ static void vl_strprintf(std::string& buffer, char const* fmt, ...) { } // Information about how to access packed array data. -// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word boundaries, -// in which case m_maskHi != 0. -template +// If underlying type is multi-word (VLVT_WDATA), the packed element might straddle word +// boundaries, in which case m_maskHi != 0. +template struct VarAccessInfo final { - T* m_datap; // Typed pointer to packed array base address - size_t m_bitOffset; // Data start location (bit offset) - size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only) - T m_maskLo; // Access mask for m_datap[m_wordOffset] - T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only) + T* m_datap; // Typed pointer to packed array base address + size_t m_bitOffset; // Data start location (bit offset) + size_t m_wordOffset; // Data start location (word offset, VLVT_WDATA only) + T m_maskLo; // Access mask for m_datap[m_wordOffset] + T m_maskHi; // Access mask for m_datap[m_wordOffset + 1] (VLVT_WDATA only) }; -template -VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { +template +VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t bitCount, + size_t addOffset) { // VarAccessInfo generation // vop - variable to access (already indexed) // bitCount - how many bits to write/read @@ -2444,8 +2438,7 @@ VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t const size_t wordBits = sizeof(T) * 8; uint32_t varBits = vop->bitSize(); - if (vop->varp()->vltype() == VLVT_REAL) - varBits *= sizeof(double) * 8; + if (vop->varp()->vltype() == VLVT_REAL) varBits *= sizeof(double) * 8; // make sure we're not trying to write outside var bounds assert(varBits > addOffset); @@ -2487,42 +2480,45 @@ VarAccessInfo vl_vpi_var_access_info(const VerilatedVpioVarBase* vop, size_t return info; } -template +template T vl_vpi_get_word_gen(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { const size_t wordBits = sizeof(T) * 8; const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); if (info.m_maskHi) - return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset) | - ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi) << (wordBits - info.m_bitOffset)); + return ((info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset) + | ((info.m_datap[info.m_wordOffset + 1] & info.m_maskHi) + << (wordBits - info.m_bitOffset)); else return (info.m_datap[info.m_wordOffset] & info.m_maskLo) >> info.m_bitOffset; } -template +template void vl_vpi_put_word_gen(const VerilatedVpioVar* vop, T word, size_t bitCount, size_t addOffset) { const size_t wordBits = sizeof(T) * 8; const VarAccessInfo info = vl_vpi_var_access_info(vop, bitCount, addOffset); if (info.m_maskHi) { - info.m_datap[info.m_wordOffset + 1] = (info.m_datap[info.m_wordOffset+1] & ~info.m_maskHi) | - ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); + info.m_datap[info.m_wordOffset + 1] + = (info.m_datap[info.m_wordOffset + 1] & ~info.m_maskHi) + | ((word >> (wordBits - info.m_bitOffset)) & info.m_maskHi); } - info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) | - ((word << info.m_bitOffset) & info.m_maskLo); + info.m_datap[info.m_wordOffset] = (info.m_datap[info.m_wordOffset] & ~info.m_maskLo) + | ((word << info.m_bitOffset) & info.m_maskLo); } // bitCount: maximum number of bits to read, will stop earlier if it reaches the var bounds // addOffset: additional read bitoffset QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t addOffset) { switch (vop->varp()->vltype()) { - case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset); - case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset); - case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset); - case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset); - case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset); - default: - VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); - return 0; + case VLVT_UINT8: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT16: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT32: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_UINT64: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + case VLVT_WDATA: return vl_vpi_get_word_gen(vop, bitCount, addOffset); + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, + vop->varp()->vltype()); + return 0; } } @@ -2531,13 +2527,14 @@ QData vl_vpi_get_word(const VerilatedVpioVarBase* vop, size_t bitCount, size_t a // addOffset: additional write bitoffset void vl_vpi_put_word(const VerilatedVpioVar* vop, QData word, size_t bitCount, size_t addOffset) { switch (vop->varp()->vltype()) { - case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; - case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; - case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; - case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; - case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; - default: - VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, vop->varp()->vltype()); + case VLVT_UINT8: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT16: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT32: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_UINT64: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + case VLVT_WDATA: vl_vpi_put_word_gen(vop, word, bitCount, addOffset); break; + default: + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vltype (%d)", __func__, + vop->varp()->vltype()); } } @@ -2732,8 +2729,8 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ vl_vpi_put_word(vop, valuep->value.vector[i].aval, 32, i * 32); return object; } else if (vop->varp()->vltype() == VLVT_UINT64 && varBits > 32) { - const QData val = (static_cast(valuep->value.vector[1].aval) << 32) | - static_cast(valuep->value.vector[0].aval); + const QData val = (static_cast(valuep->value.vector[1].aval) << 32) + | static_cast(valuep->value.vector[0].aval); vl_vpi_put_word(vop, val, 64, 0); return object; } else { diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index b0ea5f576..dfc862ced 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -960,7 +960,6 @@ void EmitCSyms::emitSymImp() { } } - putns(scopep, protect("__Vscope_" + it->second.m_scopeName)); putns(varp, ".varInsert(__Vfinal,"); putsQuoted(protect(it->second.m_varBasePretty)); diff --git a/test_regress/t/t_vpi_get.cpp b/test_regress/t/t_vpi_get.cpp index 8e2babd4f..7a9e5711f 100644 --- a/test_regress/t/t_vpi_get.cpp +++ b/test_regress/t/t_vpi_get.cpp @@ -89,7 +89,8 @@ static int _mon_check_props(TestVpiHandle& handle, int size, int direction, int int vpidir = vpi_get(vpiDirection, handle); // Don't check port directions in verilator // See issue #681 - if (!TestSimulator::is_verilator() && !TestSimulator::is_questa()) CHECK_RESULT(vpidir, direction); + if (!TestSimulator::is_verilator() && !TestSimulator::is_questa()) + CHECK_RESULT(vpidir, direction); } // check type of object @@ -120,25 +121,25 @@ int mon_check_props() { // the code that sets up the VerilatedAssertOneThread() check in // verilated_vpi.cc, it was causing the check to falsely fail // (due to m_threadid within the check not being initted yet.) - static struct params values[] - = {{"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, - {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"onetwo", {2, vpiNoDirection, 1, vpiRegArray}, {0, 0, 0, 0}}, - {"fourthreetwoone", - {2, vpiNoDirection, 0, vpiRegArray}, - {2, vpiNoDirection, 0, vpiReg}}, - {"theint", {32, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiIntVar}, {0, 0, 0, 0}}, - {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, - {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, - {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, - {"sub.subin", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, - {"sub.subout", {1, vpiOutput, 1, vpiPort}, {0, 0, 0, 0}}, - {"sub.subparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"sub.the_intf.bytesig", {8, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {"sub.the_intf.param", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"sub.the_intf.lparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, - {"twobytwo", {4, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, - {NULL, {0, 0, 0, 0}, {0, 0, 0, 0}}}; + static struct params values[] = { + {"onebit", {1, vpiNoDirection, 1, vpiReg}, {0, 0, 0, 0}}, + {"twoone", {2, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {"onetwo", {2, vpiNoDirection, 1, vpiRegArray}, {0, 0, 0, 0}}, + {"fourthreetwoone", {2, vpiNoDirection, 0, vpiRegArray}, {2, vpiNoDirection, 0, vpiReg}}, + {"theint", + {32, vpiNoDirection, 0, TestSimulator::is_verilator() ? vpiReg : vpiIntVar}, + {0, 0, 0, 0}}, + {"clk", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, + {"testin", {16, vpiInput, 0, vpiPort}, {0, 0, 0, 0}}, + {"testout", {24, vpiOutput, 0, vpiPort}, {0, 0, 0, 0}}, + {"sub.subin", {1, vpiInput, 1, vpiPort}, {0, 0, 0, 0}}, + {"sub.subout", {1, vpiOutput, 1, vpiPort}, {0, 0, 0, 0}}, + {"sub.subparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"sub.the_intf.bytesig", {8, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {"sub.the_intf.param", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"sub.the_intf.lparam", {32, vpiNoDirection, 0, vpiParameter}, {0, 0, 0, 0}}, + {"twobytwo", {4, vpiNoDirection, 0, vpiReg}, {0, 0, 0, 0}}, + {NULL, {0, 0, 0, 0}, {0, 0, 0, 0}}}; struct params* value = values; while (value->signal) { TestVpiHandle h = VPI_HANDLE(value->signal); diff --git a/test_regress/t/t_vpi_multidim.cpp b/test_regress/t/t_vpi_multidim.cpp index f66c28dec..5041703cf 100644 --- a/test_regress/t/t_vpi_multidim.cpp +++ b/test_regress/t/t_vpi_multidim.cpp @@ -28,10 +28,10 @@ #endif #include +#include #include #include #include -#include // These require the above. Comment prevents clang-format moving them #include "TestCheck.h" @@ -42,8 +42,8 @@ int errors = 0; // TEST START -void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRangeHigh, int expRangeLow) -{ +void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRangeHigh, + int expRangeLow) { const int vpitype = vpi_get(vpiType, arr_h); TEST_CHECK_EQ(vpitype, expType); const int vpisize = vpi_get(vpiSize, arr_h); @@ -64,7 +64,8 @@ void _arr_type_check(TestVpiHandle& arr_h, int expType, int expSize, int expRang } void _arr_iter_check(const char* name, int wordSize, const int* lows) { - TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TestVpiHandle arr_h + = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); TEST_CHECK_NZ(arr_h); _arr_type_check(arr_h, vpiRegArray, 4, lows[0] + 1, lows[0]); @@ -118,13 +119,15 @@ void _arr_iter_check(const char* name, int wordSize, const int* lows) { TEST_CHECK_NZ(arr_elem2_h); // first indexing yields wordSize / 2 Regs - _arr_type_check(arr_elem2_h, vpiReg, wordSize / 2, lows[3] + wordSize / 2 - 1, lows[3]); + _arr_type_check(arr_elem2_h, vpiReg, wordSize / 2, lows[3] + wordSize / 2 - 1, + lows[3]); for (int idx3 = lows[3]; idx3 < lows[3] + wordSize / 2; idx3++) { TestVpiHandle arr_elem3_h = vpi_handle_by_index(arr_elem2_h, idx3); TEST_CHECK_NZ(arr_elem3_h); { - // second indexing yields size-1 RegBits (no support for RegBit VPI type yet) + // second indexing yields size-1 RegBits (no support for RegBit VPI type + // yet) const int vpitype = vpi_get(vpiType, arr_elem3_h); if (TestSimulator::is_verilator()) { TEST_CHECK_EQ(vpitype, vpiReg); @@ -202,8 +205,8 @@ void _arr_iter_check(const char* name, int wordSize, const int* lows) { } } -void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* lows, const char *octVal_s, PLI_INT32 format) -{ +void _arr_access_format_check(TestVpiHandle& reg_h, int wordSize, const int* lows, + const char* octVal_s, PLI_INT32 format) { const int spanSize = wordSize / 2; s_vpi_value value_in; s_vpi_value value_out; @@ -230,7 +233,7 @@ void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* low uint64_t intVal; t_vpi_vecval vecVal[2]; sscanf(octSpan_s, "%" SCNo64, &intVal); - char strVal_s[spanSize + 1]; // max length of the string happens for binary + char strVal_s[spanSize + 1]; // max length of the string happens for binary if (format == vpiIntVal) { value_in.value.integer = intVal; @@ -269,23 +272,20 @@ void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* low vpi_get_value(subreg_h, &value_out); switch (format) { - case vpiIntVal: - TEST_CHECK_EQ(value_out.value.integer, value_in.value.integer); - break; - case vpiVectorVal: - if (spanSize > 32) - TEST_CHECK_EQ(value_out.value.vector[1].aval, value_in.value.vector[1].aval); - TEST_CHECK_EQ(value_out.value.vector[0].aval, value_in.value.vector[0].aval); - break; - case vpiStringVal: - TEST_CHECK_EQ(value_out.value.str[0], value_in.value.str[0] ? value_in.value.str[0] : ' '); - break; - case vpiBinStrVal: - case vpiDecStrVal: - case vpiHexStrVal: - case vpiOctStrVal: - TEST_CHECK_CSTR(value_out.value.str, value_in.value.str); - break; + case vpiIntVal: TEST_CHECK_EQ(value_out.value.integer, value_in.value.integer); break; + case vpiVectorVal: + if (spanSize > 32) + TEST_CHECK_EQ(value_out.value.vector[1].aval, value_in.value.vector[1].aval); + TEST_CHECK_EQ(value_out.value.vector[0].aval, value_in.value.vector[0].aval); + break; + case vpiStringVal: + TEST_CHECK_EQ(value_out.value.str[0], + value_in.value.str[0] ? value_in.value.str[0] : ' '); + break; + case vpiBinStrVal: + case vpiDecStrVal: + case vpiHexStrVal: + case vpiOctStrVal: TEST_CHECK_CSTR(value_out.value.str, value_in.value.str); break; } } @@ -298,22 +298,23 @@ void _arr_access_format_check(TestVpiHandle ®_h, int wordSize, const int* low std::default_random_engine rng; void _arr_access_check(const char* name, int wordSize, const int* lows) { - TestVpiHandle arr_h = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); + TestVpiHandle arr_h + = vpi_handle_by_name(const_cast(TestSimulator::rooted(name)), NULL); TEST_CHECK_NZ(arr_h); - std::uniform_int_distribution rand64( - std::numeric_limits::min(), - std::numeric_limits::max() - ); + std::uniform_int_distribution rand64(std::numeric_limits::min(), + std::numeric_limits::max()); char octVal_s[wordSize / 3 + 1]; // fill octVal_s with random octal digits if (wordSize < 64) { - sprintf(octVal_s, "%0*" PRIo64, wordSize / 3, rand64(rng) % (static_cast(1) << wordSize)); + sprintf(octVal_s, "%0*" PRIo64, wordSize / 3, + rand64(rng) % (static_cast(1) << wordSize)); } else { sprintf(octVal_s, "%0*" PRIo64, 63 / 3, rand64(rng)); - sprintf(octVal_s + 63 / 3, "%0*" PRIo64, (wordSize - 63) / 3, rand64(rng) % (static_cast(1) << (wordSize - 63))); + sprintf(octVal_s + 63 / 3, "%0*" PRIo64, (wordSize - 63) / 3, + rand64(rng) % (static_cast(1) << (wordSize - 63))); } // Assume that reading/writing to the "flattened" packed register is already tested, @@ -355,14 +356,10 @@ struct params { }; void _multidim_check() { - static struct params values[] = { - {"arr_cdata", 6, {0, 1, 2, 3}}, - {"arr_sdata", 12, {4, 5, 6, 7}}, - {"arr_idata", 30, {8, 9, 10, 11}}, - {"arr_qdata", 60, {12, 13, 14, 15}}, - {"arr_wdata", 126, {16, 17, 18, 19}}, - {nullptr, 0, {0, 0, 0, 0}} - }; + static struct params values[] + = {{"arr_cdata", 6, {0, 1, 2, 3}}, {"arr_sdata", 12, {4, 5, 6, 7}}, + {"arr_idata", 30, {8, 9, 10, 11}}, {"arr_qdata", 60, {12, 13, 14, 15}}, + {"arr_wdata", 126, {16, 17, 18, 19}}, {nullptr, 0, {0, 0, 0, 0}}}; struct params* value = values; while (value->name) { _arr_iter_check(value->name, value->wordSize, value->lows); @@ -411,7 +408,7 @@ void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; int main(int argc, char** argv) { const std::unique_ptr contextp{new VerilatedContext}; - uint64_t sim_time = 1100; // TODO + uint64_t sim_time = 1100; // TODO contextp->debug(0); contextp->commandArgs(argc, argv); From dfe28f7ed0e67f2794ddc5e167bdcfaacc40d31f Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Jan 2025 08:48:40 -0500 Subject: [PATCH 29/93] Fix misoptimizing away `$urandom` (#5703). --- Changes | 1 + src/V3AstNodeExpr.h | 2 +- test_regress/t/t_sys_rand_seed.v | 27 ++++++++++++++++++++++++++- 3 files changed, 28 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index 1ab276672..a60e8b4f0 100644 --- a/Changes +++ b/Changes @@ -23,6 +23,7 @@ Verilator 5.033 devel * Fix pattern assignment to real inside struct (#5713). * Fix %p format output for real inside struct (#5713). * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] +* Fix misoptimizing away `$urandom` (#5703). [Parker Schless] * Fix matching language extension options including dots. diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index 13382f64b..db32a6de9 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1820,7 +1820,6 @@ public: return "VL_RANDOM_SEEDED_%nq%lq(%li)"; } } - if (isWide()) { return "VL_RANDOM_%nq(%nw, %P)"; } else { @@ -1830,6 +1829,7 @@ public: bool cleanOut() const override { return false; } bool isGateOptimizable() const override { return false; } bool isPredictOptimizable() const override { return false; } + bool isPure() override { return !m_reset && !seedp(); } int instrCount() const override { return INSTR_COUNT_PLI; } bool sameNode(const AstNode* /*samep*/) const override { return true; } bool combinable(const AstRand* samep) const { diff --git a/test_regress/t/t_sys_rand_seed.v b/test_regress/t/t_sys_rand_seed.v index 549f5e8c8..3a9acec20 100644 --- a/test_regress/t/t_sys_rand_seed.v +++ b/test_regress/t/t_sys_rand_seed.v @@ -12,6 +12,9 @@ module t; int valuea; int valueb; int valuec; + int igna; + int ignb; + int ignc; initial begin // $random unlike $urandom updates the value if given @@ -32,7 +35,29 @@ module t; valuea = $urandom(10); valueb = $urandom(10); - if (valuea !== valueb) $stop; + valuec = $urandom(10); + if (valuea !== valueb && valueb != valuec) $stop; + + valuea = $urandom(10); + valueb = $urandom(11); + valuec = $urandom(12); + if (valuea == valueb && valueb == valuec) $stop; // May false fail 1 in 1^64 + + $urandom(10); + valuea = $urandom; + $urandom(10); + valueb = $urandom; + $urandom(10); + valuec = $urandom; + if (valuea != valueb && valueb != valuec) $stop; // May false fail 1 in 1^64 + + igna = $urandom(10); + valuea = $urandom; + ignb = $urandom(10); + valueb = $urandom; + ignc = $urandom(10); + valuec = $urandom; + if (valuea != valueb && valueb != valuec) $stop; // May false fail 1 in 1^64 valuea = $urandom(10); valueb = $urandom(); From d3e205f2013b4b49ed2698e39caa03f5e0ced51b Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Sat, 11 Jan 2025 18:07:52 +0100 Subject: [PATCH 30/93] Support constraints on associative array user-defined keys (#5671) (#5729) --- include/verilated_types.h | 12 +++ src/V3EmitCHeaders.cpp | 17 +++ src/V3Randomize.cpp | 42 +++++--- test_regress/t/t_constraint_assoc_arr_bad.out | 9 ++ test_regress/t/t_constraint_assoc_arr_bad.v | 77 ++++++++++++-- .../t/t_constraint_assoc_arr_others.py | 21 ++++ .../t/t_constraint_assoc_arr_others.v | 100 ++++++++++++++++++ 7 files changed, 253 insertions(+), 25 deletions(-) create mode 100755 test_regress/t/t_constraint_assoc_arr_others.py create mode 100755 test_regress/t/t_constraint_assoc_arr_others.v diff --git a/include/verilated_types.h b/include/verilated_types.h index 851366b95..c7ef5e35d 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -505,6 +505,12 @@ public: VlQueue& operator=(VlQueue&&) = default; bool operator==(const VlQueue& rhs) const { return m_deque == rhs.m_deque; } bool operator!=(const VlQueue& rhs) const { return m_deque != rhs.m_deque; } + bool operator<(const VlQueue& rhs) const { + for (int index = 0; index < m_deque.size(); ++index) { + if (m_deque[index] < rhs.m_deque[index]) return true; + } + return false; + } // Standard copy constructor works. Verilog: assoca = assocb // Also must allow conversion from a different N_MaxSize queue @@ -1364,6 +1370,12 @@ public: bool neq(const T_Value that[N_Depth]) const { return neq(*this, that); } void assign(const T_Value that[N_Depth]) { std::copy_n(that, N_Depth, m_storage); } void operator=(const T_Value that[N_Depth]) { assign(that); } + bool operator<(const VlUnpacked& that) const { + for (int index = 0; index < N_Depth; ++index) { + if (m_storage[index] < that.m_storage[index]) return true; + } + return false; + } // inside (set membership operator) bool inside(const T_Value& value) const { diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 5abde202e..9af1c604d 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -262,6 +262,23 @@ class EmitCHeader final : public EmitCConstInit { putns(sdtypep, "bool operator!=(const " + EmitCBase::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return !(*this == rhs);\n}\n"); + putns(sdtypep, "\nbool operator<(const " + EmitCBase::prefixNameProtect(sdtypep) + + "& rhs) const {\n"); + puts("return "); + puts("std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp != sdtypep->membersp()) puts(", "); + putns(itemp, itemp->nameProtect()); + } + puts(")\n < std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp != sdtypep->membersp()) puts(", "); + putns(itemp, "rhs." + itemp->nameProtect()); + } + puts(");\n"); + puts("}\n"); puts("};\n"); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 0fdb58a16..6baa90919 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -709,7 +709,7 @@ class ConstraintExprVisitor final : public VNVisitor { void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); - if (VN_IS(nodep->bitp(), CvtPackString)) { + if (VN_IS(nodep->bitp(), CvtPackString) && VN_IS(nodep->bitp()->dtypep(), BasicDType)) { AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString); const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width(); if (stringSize > 128) { @@ -724,22 +724,32 @@ class ConstraintExprVisitor final : public VNVisitor { handle.relink(idxp); editSMT(nodep, nodep->fromp(), idxp); } else { - VNRelinker handle; - const int actual_width = nodep->bitp()->width(); - std::string fmt; - // Normalize to standard bit width - if (actual_width <= 8) { - fmt = "#x%2x"; - } else if (actual_width <= 16) { - fmt = "#x%4x"; - } else { - fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; - } + if (VN_IS(nodep->bitp()->dtypep(), BasicDType) + || (VN_IS(nodep->bitp()->dtypep(), StructDType) + && VN_AS(nodep->bitp()->dtypep(), StructDType)->packed()) + || VN_IS(nodep->bitp()->dtypep(), EnumDType) + || VN_IS(nodep->bitp()->dtypep(), PackArrayDType)) { + VNRelinker handle; + const int actual_width = nodep->bitp()->width(); + std::string fmt; + // Normalize to standard bit width + if (actual_width <= 8) { + fmt = "#x%2x"; + } else if (actual_width <= 16) { + fmt = "#x%4x"; + } else { + fmt = "#x%" + std::to_string(VL_WORDS_I(actual_width) * 8) + "x"; + } - AstNodeExpr* const idxp - = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; - handle.relink(idxp); - editSMT(nodep, nodep->fromp(), idxp); + AstNodeExpr* const idxp + = new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)}; + handle.relink(idxp); + editSMT(nodep, nodep->fromp(), idxp); + } else { + nodep->bitp()->v3error( + "Illegal non-integral expression or subexpression in random constraint." + " (IEEE 1800-2023 18.3)"); + } } } void visit(AstArraySel* nodep) override { diff --git a/test_regress/t/t_constraint_assoc_arr_bad.out b/test_regress/t/t_constraint_assoc_arr_bad.out index 0e919e3b9..167603034 100644 --- a/test_regress/t/t_constraint_assoc_arr_bad.out +++ b/test_regress/t/t_constraint_assoc_arr_bad.out @@ -3,4 +3,13 @@ | ^~~~~~~~~~~~~~~~~~~~ ... For warning description see https://verilator.org/warn/CONSTRAINTIGN?v=latest ... Use "/* verilator lint_off CONSTRAINTIGN */" and lint_on around source to disable this message. +%Error: t/t_constraint_assoc_arr_bad.v:30:26: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 30 | constraint c1 { data[cl] > 0;} + | ^~ +%Error: t/t_constraint_assoc_arr_bad.v:44:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 44 | constraint c2 { foreach (data[i]) data[i] < 100; } + | ^ +%Error: t/t_constraint_assoc_arr_bad.v:58:44: Illegal non-integral expression or subexpression in random constraint. (IEEE 1800-2023 18.3) + 58 | constraint c3 { foreach (data[i]) data[i] > 0; } + | ^ %Error: Exiting due to diff --git a/test_regress/t/t_constraint_assoc_arr_bad.v b/test_regress/t/t_constraint_assoc_arr_bad.v index 7ffeba96a..644b19f30 100755 --- a/test_regress/t/t_constraint_assoc_arr_bad.v +++ b/test_regress/t/t_constraint_assoc_arr_bad.v @@ -4,8 +4,8 @@ // any use, without warranty, 2024 by PlanV GmbH. // SPDX-License-Identifier: CC0-1.0 -class AssocArrayWarningTest; - +// Long String index associative array +class AssocArrayString; rand int string_arr [string]; constraint c { @@ -14,20 +14,79 @@ class AssocArrayWarningTest; function new(); string_arr["a_very_long_string"] = 0; endfunction +endclass +class keyClass; + int id; + function new(); + id = 3; + endfunction +endclass +// Class index associative array +class AssocArrayClass; + rand bit [31:0] data [keyClass]; + keyClass cl; + // constraint c4 { foreach (data[i]) data[i] > 0;} Unsupported index type for an associative array in an iterative constraint. + constraint c1 { data[cl] > 0;} // Illegal index expression of unpacked type in constraint. + function new(); + cl = new(); + data[cl] = 32'd77; + endfunction +endclass + +typedef struct { + int a; + int b; +} UnpackedIndexType; +// Struct (unpacked) index associative array +class AssocArrayUnpackedStruct; + rand bit [31:0] data [UnpackedIndexType]; + constraint c2 { foreach (data[i]) data[i] < 100; } // Illegal non-integral expression in random constraint. + + function new(); + UnpackedIndexType idx; + idx.a = 1; + idx.b = 2; + data[idx] = 32'd25; + endfunction +endclass + +// Array (unpacked) index associative array +typedef logic [2:0] IndexArrayType[3]; +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] > 0; } + + function new(); + IndexArrayType idx; + for (int j = 0; j < 4; j++) begin + idx[j] = 3'd0; + end + data[idx] = 32'd75; + endfunction endclass module t_constraint_assoc_arr_bad; - AssocArrayWarningTest test_obj; + AssocArrayString test_str; + AssocArrayClass test_cls; + AssocArrayUnpackedStruct test_unp_struct; + AssocArrayArrayIndex test_unp_arr; + int success = 0; initial begin - test_obj = new(); - repeat(2) begin - int success; - success = test_obj.randomize(); - if (success != 1) $stop; - end + test_str = new(); + test_cls = new(); + test_unp_struct = new(); + test_unp_arr = new(); + + success += test_str.randomize(); + success += test_cls.randomize(); + success += test_unp_struct.randomize(); + success += test_unp_arr.randomize(); + + if(success != 4) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_constraint_assoc_arr_others.py b/test_regress/t/t_constraint_assoc_arr_others.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.py @@ -0,0 +1,21 @@ +#!/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('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_assoc_arr_others.v b/test_regress/t/t_constraint_assoc_arr_others.v new file mode 100755 index 000000000..badbc3387 --- /dev/null +++ b/test_regress/t/t_constraint_assoc_arr_others.v @@ -0,0 +1,100 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +// Enum-based associative array +typedef enum { RED, GREEN, YELLOW } color_t; + +class AssocArrayEnum; + rand bit [7:0] colors [color_t]; + constraint c1 { foreach (colors[i]) colors[i] == 4; } + + function new(); + colors[RED] = 8'd5; + colors[GREEN] = 8'd10; + colors[YELLOW] = 8'd15; + endfunction + + function void self_check(); + foreach (colors[i]) begin + if (colors[i] != 4) $stop; + end + endfunction +endclass + +// Struct (packed) index associative array +typedef struct packed { + bit [2:0] high; + bit [1:0] low; +} PackedIndexType; + +class AssocArrayPackedStruct; + rand bit [31:0] data [PackedIndexType]; + constraint c2 { foreach (data[i]) data[i] == 100; } + + function new(); + PackedIndexType idx; + idx.high = 3'd1; + idx.low = 2'd1; + data[idx] = 32'd50; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 100) $stop; + end + endfunction +endclass + +// Array (packed) index associative array +typedef logic [2:0][7:0] IndexArrayType; + +class AssocArrayArrayIndex; + rand bit [31:0] data [IndexArrayType]; + constraint c3 { foreach (data[i]) data[i] == 0; } + + function new(); + IndexArrayType idx; + idx = 0; + data[idx] = 32'd75; + endfunction + + function void self_check(); + foreach (data[i]) begin + if (data[i] != 0) $stop; + end + endfunction +endclass + +module t_constraint_assoc_array_others; + + AssocArrayEnum enum_arr; + AssocArrayPackedStruct packed_arr; + AssocArrayArrayIndex array_arr; + int success = 0; + + initial begin + // Create instances of the classes + enum_arr = new(); + packed_arr = new(); + array_arr = new(); + + // Randomization and self-check + success = enum_arr.randomize(); + if (success != 1) $stop; + enum_arr.self_check(); + + success = packed_arr.randomize(); + if (success != 1) $stop; + packed_arr.self_check(); + + success = array_arr.randomize(); + if (success != 1) $stop; + array_arr.self_check(); + + $write("*-* All Finished *-*"); + $finish; + end +endmodule From 28a59a2b21859f6a41b49a25d9bf6b8537d45169 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Jan 2025 12:16:46 -0500 Subject: [PATCH 31/93] Update gtkwave from upstream. --- include/gtkwave/fstapi.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/gtkwave/fstapi.c b/include/gtkwave/fstapi.c index ed818bfb8..9845edacd 100644 --- a/include/gtkwave/fstapi.c +++ b/include/gtkwave/fstapi.c @@ -3910,7 +3910,7 @@ char *pnt = buf; int len = 0; /* zero is illegal for a value...it is assumed they start at one */ -while (value && len <= 14) +while (value && len < 14) { value--; ++len; From f8dd65c7cd024d8c9101d7be9007afb5f8cc6f2a Mon Sep 17 00:00:00 2001 From: Andrew Nolte Date: Sat, 11 Jan 2025 12:28:34 -0500 Subject: [PATCH 32/93] Add `--public-ignore` to ignore public metacomments (#7819) --- Changes | 3 ++- bin/verilator | 3 ++- docs/guide/exe_verilator.rst | 17 ++++++++++++---- src/V3Options.cpp | 3 ++- src/V3Options.h | 8 +++++--- src/V3PreProc.cpp | 2 +- test_regress/t/t_vpi_public_depth.cpp | 11 ++++++++++ test_regress/t/t_vpi_public_depth.v | 3 ++- test_regress/t/t_vpi_public_off.py | 29 +++++++++++++++++++++++++++ 9 files changed, 67 insertions(+), 12 deletions(-) create mode 100755 test_regress/t/t_vpi_public_off.py diff --git a/Changes b/Changes index a60e8b4f0..93be6a8bd 100644 --- a/Changes +++ b/Changes @@ -13,7 +13,8 @@ Verilator 5.033 devel **Minor:** -* Add COVERIGN warning, as a more specific UNSUPPORTED error. +* Add `COVERIGN` warning, as a more specific UNSUPPORTED error. +* Add `--public-ignore` to ignore public metacomments (#7819). [Andrew Nolte] * Support generated classes (#5665). [Shou-Li Hsu] * Support `+incdir` with multiple directories. * Support side effects of form 'variable[index_function()]++'. diff --git a/bin/verilator b/bin/verilator index 769f3fb03..756344a25 100755 --- a/bin/verilator +++ b/bin/verilator @@ -431,8 +431,9 @@ detailed descriptions of these arguments. --protect-lib Create a DPI protected library --public Mark signals as public; see docs --public-depth Mark public to specified module depth - --public-params Mark all parameters as public_flat --public-flat-rw Mark all variables, etc as public_flat_rw + --public-ignore Ignore all public comment markings + --public-params Mark all parameters as public_flat -pvalue+= Overwrite toplevel parameter --quiet Minimize additional printing --quiet-exit Don't print the command on failure diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 1f653d652..d9b4075ab 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1211,6 +1211,13 @@ Summary: module specifically enabled it with :option:`/*verilator&32;inline_module*/`. + +.. option:: --public-depth + + Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules. + It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the + a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public. + .. option:: --public-flat-rw Declares all variables, ports, and wires public as if they had @@ -1222,11 +1229,12 @@ Summary: marking only those signals that need public_flat_rw is typically significantly better performing. -.. option:: --public-depth +.. option:: --public-ignore - Enables public as with :vlopt:`--public-flat-rw`, but only to the specified depth of modules. - It operates at the module maximum level, so if a module's cells are A.B.X and A.X, the - a --public-depth 3 must be used to make module X public, and both A.B.X and A.X will be public. + Ignore all :code:`/*verilator public* */` metacomments. This is useful + for speed-optimizing VPI builds where VPI is not being used. This only + affects metacomments; options such as :vlopt:`--public`, + :vlopt:`--public-depth`, etc. work normally. .. option:: --public-params @@ -1234,6 +1242,7 @@ Summary: :code:`/*verilator public_flat_rd*/` metacomments. + .. option:: -pvalue+= Overwrites the given parameter(s) of the top-level module. See diff --git a/src/V3Options.cpp b/src/V3Options.cpp index aad0856b9..f4dc0f573 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1510,8 +1510,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_publicFlatRW = flag; v3Global.dpi(true); }); + DECL_OPTION("-public-ignore", CbOnOff, [this](bool flag) { m_publicIgnore = flag; }); DECL_OPTION("-public-params", CbOnOff, [this](bool flag) { - m_public_params = flag; + m_publicParams = flag; v3Global.dpi(true); }); DECL_OPTION("-quiet", CbOnOff, [this](bool flag) { diff --git a/src/V3Options.h b/src/V3Options.h index c1ed66ec9..b19c6dfad 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -273,7 +273,8 @@ private: bool m_protectIds = false; // main switch: --protect-ids bool m_public = false; // main switch: --public bool m_publicFlatRW = false; // main switch: --public-flat-rw - bool m_public_params = false; // main switch: --public-params + bool m_publicIgnore = false; // main switch: --public-ignore + bool m_publicParams = false; // main switch: --public-params bool m_quietExit = false; // main switch: --quiet-exit bool m_quietStats = false; // main switch: --quiet-stats bool m_relativeIncludes = false; // main switch: --relative-includes @@ -541,10 +542,11 @@ public: bool usesProfiler() const { return profExec() || profPgo(); } bool protectIds() const VL_MT_SAFE { return m_protectIds; } bool allPublic() const { return m_public; } - bool publicParams() const { return m_public_params; } + bool publicParams() const { return m_publicParams; } + bool publicOff() const { return m_publicIgnore; } bool publicFlatRW() const { return m_publicFlatRW; } int publicDepth() const { return m_publicDepth; } - bool anyPublicFlat() const { return m_public_params || m_publicFlatRW || m_publicDepth; } + bool anyPublicFlat() const { return m_publicParams || m_publicFlatRW || m_publicDepth; } bool lintOnly() const VL_MT_SAFE { return m_lintOnly; } bool ignc() const { return m_ignc; } bool quietExit() const VL_MT_SAFE { return m_quietExit; } diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 63894e72a..203faf5bf 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -486,7 +486,7 @@ void V3PreProcImp::comment(const string& text) { //} // else ignore the comment we don't recognize } // else no assertions - } else if (vlcomment) { + } else if (vlcomment && !(v3Global.opt.publicOff() && VString::startsWith(cmd, "public"))) { if (VString::startsWith(cmd, "public_flat_rw")) { // "/*verilator public_flat_rw @(foo) */" -> "/*verilator public_flat_rw*/ @(foo)" string::size_type endOfCmd = std::strlen("public_flat_rw"); diff --git a/test_regress/t/t_vpi_public_depth.cpp b/test_regress/t/t_vpi_public_depth.cpp index cb4da699c..e1bf6e2a6 100644 --- a/test_regress/t/t_vpi_public_depth.cpp +++ b/test_regress/t/t_vpi_public_depth.cpp @@ -26,6 +26,8 @@ #include "Vt_vpi_public_depth__Dpi.h" #elif defined(T_VPI_PUBLIC_DEPTH_OFF) #include "Vt_vpi_public_depth_off__Dpi.h" +#elif defined(T_VPI_PUBLIC_OFF) +#include "Vt_vpi_public_off__Dpi.h" #else #error "Bad test" #endif @@ -87,6 +89,15 @@ int mon_check() { it.freed(); // IEEE 37.2.2 vpi_scan at end does a vpi_release_handle CHECK_RESULT_Z(topmod_done_should_be_0); + TestVpiHandle mod_a = vpi_handle_by_name(const_cast("\\mod.a "), topmod); +#if defined(T_VPI_PUBLIC_OFF) + // metacomment from module A should be ignored + CHECK_RESULT_Z(mod_a); + return 0; +#endif + + CHECK_RESULT_NZ(mod_a); + TestVpiHandle it2 = vpi_iterate(vpiModule, topmod); CHECK_RESULT_NZ(it2); diff --git a/test_regress/t/t_vpi_public_depth.v b/test_regress/t/t_vpi_public_depth.v index eeaedae9a..3766dfec3 100644 --- a/test_regress/t/t_vpi_public_depth.v +++ b/test_regress/t/t_vpi_public_depth.v @@ -65,7 +65,8 @@ module A(/*AUTOARG*/ clk, a, b ); - input clk; + // this comment should get ignored for public-ignore + input clk /* verilator public_flat_rw */; input a, b; output x; diff --git a/test_regress/t/t_vpi_public_off.py b/test_regress/t/t_vpi_public_off.py new file mode 100755 index 000000000..34b624b8e --- /dev/null +++ b/test_regress/t/t_vpi_public_off.py @@ -0,0 +1,29 @@ +#!/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('simulator') +test.pli_filename = "t/t_vpi_public_depth.cpp" +test.top_filename = "t/t_vpi_public_depth.v" + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + iv_flags2=["-g2005-sv"], + verilator_flags2=[ + "+define+USE_DOLLAR_C32 --exe --vpi --no-l2name", + test.pli_filename, + "--public-depth 1 --public-ignore", + ], + make_flags=['CPPFLAGS_ADD=-DTEST_VPI_PUBLIC_OFF']) + +test.execute(use_libvpi=True) + +test.passes() From 529b8337ecbd9e5dedf7e178f16c1eb0ebf7a9b0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Jan 2025 12:30:07 -0500 Subject: [PATCH 33/93] Commentary: Changes update --- Changes | 9 ++++++++- docs/spelling.txt | 1 + 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 93be6a8bd..f5ab6f4e2 100644 --- a/Changes +++ b/Changes @@ -15,16 +15,23 @@ Verilator 5.033 devel * Add `COVERIGN` warning, as a more specific UNSUPPORTED error. * Add `--public-ignore` to ignore public metacomments (#7819). [Andrew Nolte] +* Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton] +* Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki] * Support generated classes (#5665). [Shou-Li Hsu] +* Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang] * Support `+incdir` with multiple directories. * Support side effects of form 'variable[index_function()]++'. +* Improve VPI write errors (#5712). [Andrew Nolte] * Fix error message when call task as a function (#3089). [Matthew Ballance] +* Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] +* Fix mis-optimizing away `$urandom` (#5703). [Parker Schless] +* Fix packages with `--public-depth 1` (#5708). [Andrew Nolte] * Fix V3Simulate constant reuse (#5709). [Geza Lore] * Fix man pages what-is section (#5710). [Ahmed El-Mahmoudy] * Fix pattern assignment to real inside struct (#5713). * Fix %p format output for real inside struct (#5713). * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] -* Fix misoptimizing away `$urandom` (#5703). [Parker Schless] +* Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] * Fix matching language extension options including dots. diff --git a/docs/spelling.txt b/docs/spelling.txt index 9270968b7..ca586f3dd 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -362,6 +362,7 @@ Sanguinetti Sanjay Sasselli Scharrer +Schless Seitz Shahid Shahrestani From 70814fa11a242ba2ba5952f710779cdb8132a374 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Jan 2025 12:33:58 -0500 Subject: [PATCH 34/93] Commentary: Add `--public-ignore` to ignore public metacomments (#5716). --- Changes | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Changes b/Changes index f5ab6f4e2..5d5ab3be9 100644 --- a/Changes +++ b/Changes @@ -14,7 +14,7 @@ Verilator 5.033 devel **Minor:** * Add `COVERIGN` warning, as a more specific UNSUPPORTED error. -* Add `--public-ignore` to ignore public metacomments (#7819). [Andrew Nolte] +* Add `--public-ignore` to ignore public metacomments (#5716). [Andrew Nolte] * Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton] * Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki] * Support generated classes (#5665). [Shou-Li Hsu] From fdf29b970f071e7346739af613c24cc606a10728 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 11 Jan 2025 15:27:11 -0500 Subject: [PATCH 35/93] Fix VL_UNLIKELY with constructors --- src/V3EmitCFunc.h | 4 ++-- src/V3Width.cpp | 2 +- test_regress/t/t_hier_block_struct_nohier.py | 19 +++++++++++++++++++ test_regress/t/t_param_array6_noslice.py | 19 +++++++++++++++++++ 4 files changed, 41 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_hier_block_struct_nohier.py create mode 100755 test_regress/t/t_param_array6_noslice.py diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 40c7e5423..e79109502 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -968,10 +968,10 @@ public: putns(nodep, "if ("); if (!nodep->branchPred().unknown()) { puts(nodep->branchPred().ascii()); - puts("("); + puts("(("); // Two parens, so that VL_UNLIKELY((class)) works } iterateAndNextConstNull(nodep->condp()); - if (!nodep->branchPred().unknown()) puts(")"); + if (!nodep->branchPred().unknown()) puts("))"); puts(") {\n"); iterateAndNextConstNull(nodep->thensp()); puts("}"); diff --git a/src/V3Width.cpp b/src/V3Width.cpp index 0dca57d26..7a47de1a7 100644 --- a/src/V3Width.cpp +++ b/src/V3Width.cpp @@ -4574,8 +4574,8 @@ class WidthVisitor final : public VNVisitor { if (patp) { // Don't want the RHS an array - patp->dtypep(arrayDtp->subDTypep()); allConstant &= VN_IS(patp->lhssp(), Const); + patp->dtypep(arrayDtp->subDTypep()); AstNodeExpr* const valuep = patternMemberValueIterate(patp); if (VN_IS(arrayDtp, UnpackArrayDType)) { if (!newp) { diff --git a/test_regress/t/t_hier_block_struct_nohier.py b/test_regress/t/t_hier_block_struct_nohier.py new file mode 100755 index 000000000..8545961d7 --- /dev/null +++ b/test_regress/t/t_hier_block_struct_nohier.py @@ -0,0 +1,19 @@ +#!/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('simulator') +test.top_filename = "t/t_hier_block_struct.v" + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_param_array6_noslice.py b/test_regress/t/t_param_array6_noslice.py new file mode 100755 index 000000000..79f955063 --- /dev/null +++ b/test_regress/t/t_param_array6_noslice.py @@ -0,0 +1,19 @@ +#!/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('simulator') +test.top_filename = "t/t_param_array6.v" + +test.compile(verilator_flags2=['-fno-slice']) + +test.execute() + +test.passes() From dc43071f1c455f6ef4c6b159d7bd2231e6d59844 Mon Sep 17 00:00:00 2001 From: Ethan Sifferman Date: Sat, 11 Jan 2025 19:53:11 -0800 Subject: [PATCH 36/93] Improve `resetall support (#5728) (#5730) --- src/verilog.l | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/verilog.l b/src/verilog.l index b1a536557..5aa52e4ed 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -1062,7 +1062,11 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "`protect" { FL_FWD; FL_BRK; } "`remove_gatenames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility "`remove_netnames" { FL_FWD; FL_BRK; } // Verilog-XL compatibility - "`resetall" { FL; PARSEP->lexFileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE, true); + "`resetall" { FL; + PARSEP->lexFileline()->warnOn(V3ErrorCode::I_DEF_NETTYPE_WIRE, true); + v3Global.rootp()->timeInit(); + PARSEP->lexFileline()->celldefineOn(false); + PARSEP->unconnectedDrive(VOptionBool::OPT_DEFAULT_FALSE); return yaT_RESETALL; } // Rest handled by preproc "`suppress_faults" { FL_FWD; FL_BRK; } // Verilog-XL compatibility "`timescale"{ws}+[^\n\r]* { FL; PARSEP->lexTimescaleParse(yylval.fl, From 20faa9946471262f657f2012beb04e91a4fe731b Mon Sep 17 00:00:00 2001 From: Natan Kreimer <94777623+natankreimer@users.noreply.github.com> Date: Mon, 13 Jan 2025 14:40:34 +0200 Subject: [PATCH 37/93] Fix VPI iteration over hierarchy (#5314) (#5731) --- docs/CONTRIBUTORS | 1 + src/V3EmitCSyms.cpp | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index d6ad08718..da6fa5589 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -156,6 +156,7 @@ Mladen Slijepcevic Morten Borup Petersen Mostafa Gamal Nandu Raj +Natan Kreimer Nathan Graybeal Nathan Kohagen Nathan Myers diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index dfc862ced..c05aa2ae7 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -181,7 +181,6 @@ class EmitCSyms final : EmitCBaseVisitorConst { /// Then add/update entry in m_scopeNames if not already there void varHierarchyScopes(string scp) { - // we want no result to be -1, so use ints string::size_type prd_pos = scp.rfind('.'); string::size_type dot_pos = scp.rfind("__DOT__"); @@ -195,7 +194,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { } // resize and advance pointers - if (prd_pos < dot_pos && dot_pos != string::npos) { + if ((prd_pos < dot_pos || prd_pos == string::npos) && dot_pos != string::npos) { scp.resize(dot_pos); dot_pos = scp.rfind("__DOT__"); } else { From dddc1b5b4d25eb3696d1aa18fae3704d08205249 Mon Sep 17 00:00:00 2001 From: Andrew Nolte Date: Thu, 16 Jan 2025 15:02:36 -0500 Subject: [PATCH 38/93] Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732) --- src/V3EmitCSyms.cpp | 31 +- test_regress/t/t_vpi_dump.cpp | 7 +- test_regress/t/t_vpi_dump.out | 380 ++++++++----------- test_regress/t/t_vpi_dump.v | 26 +- test_regress/t/t_vpi_dump_missing_scopes.out | 18 +- test_regress/t/t_vpi_dump_no_inline.py | 32 ++ 6 files changed, 243 insertions(+), 251 deletions(-) create mode 100755 test_regress/t/t_vpi_dump_no_inline.py diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index c05aa2ae7..1eaeea18c 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -106,7 +106,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { std::vector m_modVars; // Each public {mod,var} std::map m_scopeFuncs; // Each {scope,dpi-export-func} std::map m_scopeVars; // Each {scope,public-var} - ScopeNames m_scopeNames; // Each unique AstScopeName + ScopeNames m_scopeNames; // Each unique AstScopeName. Dpi scopes added later + ScopeNames m_dpiScopeNames; // Each unique AstScopeName for DPI export ScopeNames m_vpiScopeCandidates; // All scopes for VPI ScopeNameHierarchy m_vpiScopeHierarchy; // The actual hierarchy of scopes int m_coverBins = 0; // Coverage bin number @@ -208,7 +209,7 @@ class EmitCSyms final : EmitCBaseVisitorConst { void varsExpand() { // We didn't have all m_scopes loaded when we encountered variables, so expand them now // It would be less code if each module inserted its own variables. - // Someday. For now public isn't common. + // Someday. for (std::vector::iterator itsc = m_scopes.begin(); itsc != m_scopes.end(); ++itsc) { AstScope* const scopep = itsc->first; @@ -282,6 +283,15 @@ class EmitCSyms final : EmitCBaseVisitorConst { if (v3Global.opt.vpi()) buildVpiHierarchy(); + if (v3Global.dpi()) { + // add dpi scopes to m_scopeNames if not already there + for (const auto& scp : m_dpiScopeNames) { + if (m_scopeNames.find(scp.first) == m_scopeNames.end()) { + m_scopeNames.emplace(scp.first, scp.second); + } + } + } + // Sort by names, so line/process order matters less stable_sort(m_scopes.begin(), m_scopes.end(), CmpName()); stable_sort(m_dpis.begin(), m_dpis.end(), CmpDpi()); @@ -330,7 +340,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { const int timeunit = m_modp->timeunit().powerOfTen(); m_vpiScopeCandidates.emplace(scopeSymString(nodep->name()), ScopeData{nodep, scopeSymString(nodep->name()), - name_pretty, "", timeunit, type}); + name_pretty, nodep->modp()->origName(), + timeunit, type}); } iterateChildrenConst(nodep); } @@ -339,19 +350,19 @@ class EmitCSyms final : EmitCBaseVisitorConst { // UINFO(9, "scnameins sp " << nodep->name() << " sp " << nodep->scopePrettySymName() // << " ss" << name << endl); const int timeunit = m_modp ? m_modp->timeunit().powerOfTen() : 0; - m_scopeNames.emplace(name, ScopeData{nodep, name, nodep->scopePrettySymName(), "", - timeunit, "SCOPE_OTHER"}); + m_dpiScopeNames.emplace(name, ScopeData{nodep, name, nodep->scopePrettySymName(), "", + timeunit, "SCOPE_OTHER"}); if (nodep->dpiExport()) { UASSERT_OBJ(m_cfuncp, nodep, "ScopeName not under DPI function"); m_scopeFuncs.emplace(name + " " + m_cfuncp->name(), ScopeFuncData(nodep, m_cfuncp, m_modp)); } else { - if (m_scopeNames.find(nodep->scopeDpiName()) == m_scopeNames.end()) { + if (m_dpiScopeNames.find(nodep->scopeDpiName()) == m_dpiScopeNames.end()) { // cppcheck-suppress stlFindInsert - m_scopeNames.emplace(nodep->scopeDpiName(), - ScopeData{nodep, nodep->scopeDpiName(), - nodep->scopePrettyDpiName(), "", timeunit, - "SCOPE_OTHER"}); + m_dpiScopeNames.emplace(nodep->scopeDpiName(), + ScopeData{nodep, nodep->scopeDpiName(), + nodep->scopePrettyDpiName(), "", timeunit, + "SCOPE_OTHER"}); } } } diff --git a/test_regress/t/t_vpi_dump.cpp b/test_regress/t/t_vpi_dump.cpp index 828c757b5..3a78e6c97 100644 --- a/test_regress/t/t_vpi_dump.cpp +++ b/test_regress/t/t_vpi_dump.cpp @@ -127,11 +127,12 @@ void modDump(TestVpiHandle& it, int n) { const int type = vpi_get(vpiType, hndl); const char* name = vpi_get_str(vpiName, hndl); const char* fullname = vpi_get_str(vpiFullName, hndl); - printf("%s (%s) %s\n", name, strFromVpiObjType(type), fullname); + printf("%s (%s) %s ", name, strFromVpiObjType(type), fullname); if (type == vpiParameter || type == vpiConstType) { - for (int i = 0; i < n + 1; i++) printf(" "); - printf("vpiConstType=%s\n", strFromVpiConstType(vpi_get(vpiConstType, hndl))); + printf(" vpiConstType=%s", strFromVpiConstType(vpi_get(vpiConstType, hndl))); } + if (type == vpiModule) { printf(" vpiDefName=%s", vpi_get_str(vpiDefName, hndl)); } + printf("\n"); if (iterate_over.find(type) == iterate_over.end()) { continue; } for (int type : iterate_over.at(type)) { diff --git a/test_regress/t/t_vpi_dump.out b/test_regress/t/t_vpi_dump.out index bf2aa75bf..9bacaf77b 100644 --- a/test_regress/t/t_vpi_dump.out +++ b/test_regress/t/t_vpi_dump.out @@ -1,307 +1,255 @@ -t (vpiModule) t +t (vpiModule) t vpiDefName=t vpiReg: - LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND (vpiReg) t.LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND - a (vpiReg) TOP.a - bus1 (vpiReg) t.bus1 - clk (vpiReg) TOP.clk - count (vpiReg) t.count - do_generate (vpiParameter) t.do_generate - vpiConstType=vpiDecConst - fourthreetwoone (vpiRegArray) t.fourthreetwoone + LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND (vpiReg) t.LONGSTART_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_a_very_long_name_which_will_get_hashed_LONGEND + a (vpiReg) TOP.a + bus1 (vpiReg) t.bus1 + clk (vpiReg) TOP.clk + count (vpiReg) t.count + do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst + fourthreetwoone (vpiRegArray) t.fourthreetwoone vpiReg: - fourthreetwoone (vpiReg) t.fourthreetwoone[3] - fourthreetwoone (vpiReg) t.fourthreetwoone[4] - half_count (vpiReg) t.half_count - long_int (vpiParameter) t.long_int - vpiConstType=vpiDecConst - onebit (vpiReg) t.onebit - quads (vpiRegArray) t.quads + fourthreetwoone (vpiReg) t.fourthreetwoone[3] + fourthreetwoone (vpiReg) t.fourthreetwoone[4] + half_count (vpiReg) t.half_count + long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst + onebit (vpiReg) t.onebit + quads (vpiRegArray) t.quads vpiReg: - quads (vpiReg) t.quads[3] - quads (vpiReg) t.quads[2] - real1 (vpiRealVar) t.real1 - status (vpiReg) t.status - str1 (vpiStringVar) t.str1 - text (vpiReg) t.text - text_byte (vpiReg) t.text_byte - text_half (vpiReg) t.text_half - text_long (vpiReg) t.text_long - text_word (vpiReg) t.text_word - twoone (vpiReg) t.twoone - x (vpiReg) TOP.x + quads (vpiReg) t.quads[3] + quads (vpiReg) t.quads[2] + real1 (vpiRealVar) t.real1 + status (vpiReg) t.status + str1 (vpiStringVar) t.str1 + text (vpiReg) t.text + text_byte (vpiReg) t.text_byte + text_half (vpiReg) t.text_half + text_long (vpiReg) t.text_long + text_word (vpiReg) t.text_word + twoone (vpiReg) t.twoone + x (vpiReg) TOP.x vpiParameter: - do_generate (vpiParameter) t.do_generate - vpiConstType=vpiDecConst - long_int (vpiParameter) t.long_int - vpiConstType=vpiDecConst + do_generate (vpiParameter) t.do_generate vpiConstType=vpiDecConst + long_int (vpiParameter) t.long_int vpiConstType=vpiDecConst vpiInternalScope: - arr[1] (vpiGenScope) t.arr[1] + arr[1] (vpiGenScope) t.arr[1] vpiReg: vpiParameter: vpiInternalScope: - arr (vpiModule) t.arr[1].arr + arr (vpiModule) t.arr[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.arr[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.arr[1].arr.check - rfr (vpiReg) t.arr[1].arr.rfr - sig (vpiReg) t.arr[1].arr.sig - verbose (vpiReg) t.arr[1].arr.verbose + LENGTH (vpiParameter) t.arr[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.arr[1].arr.check + rfr (vpiReg) t.arr[1].arr.rfr + sig (vpiReg) t.arr[1].arr.sig + verbose (vpiReg) t.arr[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.arr[1].arr.LENGTH - vpiConstType=vpiDecConst - arr[2] (vpiGenScope) t.arr[2] + LENGTH (vpiParameter) t.arr[1].arr.LENGTH vpiConstType=vpiDecConst + arr[2] (vpiGenScope) t.arr[2] vpiReg: vpiParameter: vpiInternalScope: - arr (vpiModule) t.arr[2].arr + arr (vpiModule) t.arr[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.arr[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.arr[2].arr.check - rfr (vpiReg) t.arr[2].arr.rfr - sig (vpiReg) t.arr[2].arr.sig - verbose (vpiReg) t.arr[2].arr.verbose + LENGTH (vpiParameter) t.arr[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.arr[2].arr.check + rfr (vpiReg) t.arr[2].arr.rfr + sig (vpiReg) t.arr[2].arr.sig + verbose (vpiReg) t.arr[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.arr[2].arr.LENGTH - vpiConstType=vpiDecConst - cond_scope (vpiGenScope) t.cond_scope + LENGTH (vpiParameter) t.arr[2].arr.LENGTH vpiConstType=vpiDecConst + cond_scope (vpiGenScope) t.cond_scope vpiReg: - scoped_wire (vpiParameter) t.cond_scope.scoped_wire - vpiConstType=vpiDecConst + scoped_wire (vpiParameter) t.cond_scope.scoped_wire vpiConstType=vpiDecConst vpiParameter: - scoped_wire (vpiParameter) t.cond_scope.scoped_wire - vpiConstType=vpiDecConst + scoped_wire (vpiParameter) t.cond_scope.scoped_wire vpiConstType=vpiDecConst vpiInternalScope: - scoped_sub (vpiModule) t.cond_scope.scoped_sub + scoped_sub (vpiModule) t.cond_scope.scoped_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.cond_scope.scoped_sub.subsig1 - subsig2 (vpiReg) t.cond_scope.scoped_sub.subsig2 + subsig1 (vpiReg) t.cond_scope.scoped_sub.subsig1 + subsig2 (vpiReg) t.cond_scope.scoped_sub.subsig2 vpiParameter: - sub_wrap_gen (vpiModule) t.cond_scope.sub_wrap_gen + sub_wrap_gen (vpiModule) t.cond_scope.sub_wrap_gen vpiDefName=ModSubWrapper vpiReg: vpiParameter: vpiInternalScope: - my_sub (vpiModule) t.cond_scope.sub_wrap_gen.my_sub + my_sub (vpiModule) t.cond_scope.sub_wrap_gen.my_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig1 - subsig2 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig2 + subsig1 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig1 + subsig2 (vpiReg) t.cond_scope.sub_wrap_gen.my_sub.subsig2 vpiParameter: - intf_arr[0] (vpiModule) t.intf_arr[0] + intf_arr[0] (vpiModule) t.intf_arr[0] vpiDefName=TestInterface vpiReg: - addr (vpiReg) t.intf_arr[0].addr + addr (vpiReg) t.intf_arr[0].addr vpiParameter: - intf_arr[1] (vpiModule) t.intf_arr[1] + intf_arr[1] (vpiModule) t.intf_arr[1] vpiDefName=TestInterface vpiReg: - addr (vpiReg) t.intf_arr[1].addr + addr (vpiReg) t.intf_arr[1].addr vpiParameter: - outer_scope[1] (vpiGenScope) t.outer_scope[1] + outer_scope[1] (vpiGenScope) t.outer_scope[1] vpiReg: - scoped_param (vpiParameter) t.outer_scope[1].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[1].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[1].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[1].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[1].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[1].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[1].arr + arr (vpiModule) t.outer_scope[1].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[1].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[1].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[2].arr + arr (vpiModule) t.outer_scope[1].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[1].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[1].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[1].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[1].inner_scope[3].arr + arr (vpiModule) t.outer_scope[1].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[1].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[1].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[1].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[1].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[1].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[1].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[1].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[1].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - outer_scope[2] (vpiGenScope) t.outer_scope[2] + LENGTH (vpiParameter) t.outer_scope[1].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + outer_scope[2] (vpiGenScope) t.outer_scope[2] vpiReg: - scoped_param (vpiParameter) t.outer_scope[2].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[2].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[2].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[2].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[2].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[2].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[1].arr + arr (vpiModule) t.outer_scope[2].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[2].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[2].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[2].arr + arr (vpiModule) t.outer_scope[2].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[2].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[2].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[2].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[2].inner_scope[3].arr + arr (vpiModule) t.outer_scope[2].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[2].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[2].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[2].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[2].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[2].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[2].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[2].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[2].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - outer_scope[3] (vpiGenScope) t.outer_scope[3] + LENGTH (vpiParameter) t.outer_scope[2].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + outer_scope[3] (vpiGenScope) t.outer_scope[3] vpiReg: - scoped_param (vpiParameter) t.outer_scope[3].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[3].scoped_param vpiConstType=vpiDecConst vpiParameter: - scoped_param (vpiParameter) t.outer_scope[3].scoped_param - vpiConstType=vpiDecConst + scoped_param (vpiParameter) t.outer_scope[3].scoped_param vpiConstType=vpiDecConst vpiInternalScope: - inner_scope[1] (vpiGenScope) t.outer_scope[3].inner_scope[1] + inner_scope[1] (vpiGenScope) t.outer_scope[3].inner_scope[1] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[1].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[1].arr + arr (vpiModule) t.outer_scope[3].inner_scope[1].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[1].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[1].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[1].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[1].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[1].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[1].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[1].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[1].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[2] (vpiGenScope) t.outer_scope[3].inner_scope[2] + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[1].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[2] (vpiGenScope) t.outer_scope[3].inner_scope[2] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[2].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[2].arr + arr (vpiModule) t.outer_scope[3].inner_scope[2].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[2].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[2].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[2].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[2].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[2].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[2].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[2].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[2].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH - vpiConstType=vpiDecConst - inner_scope[3] (vpiGenScope) t.outer_scope[3].inner_scope[3] + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[2].arr.LENGTH vpiConstType=vpiDecConst + inner_scope[3] (vpiGenScope) t.outer_scope[3].inner_scope[3] vpiReg: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiParameter: - scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner - vpiConstType=vpiDecConst + scoped_param_inner (vpiParameter) t.outer_scope[3].inner_scope[3].scoped_param_inner vpiConstType=vpiDecConst vpiInternalScope: - arr (vpiModule) t.outer_scope[3].inner_scope[3].arr + arr (vpiModule) t.outer_scope[3].inner_scope[3].arr vpiDefName=ModArr vpiReg: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - check (vpiReg) t.outer_scope[3].inner_scope[3].arr.check - rfr (vpiReg) t.outer_scope[3].inner_scope[3].arr.rfr - sig (vpiReg) t.outer_scope[3].inner_scope[3].arr.sig - verbose (vpiReg) t.outer_scope[3].inner_scope[3].arr.verbose + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + check (vpiReg) t.outer_scope[3].inner_scope[3].arr.check + rfr (vpiReg) t.outer_scope[3].inner_scope[3].arr.rfr + sig (vpiReg) t.outer_scope[3].inner_scope[3].arr.sig + verbose (vpiReg) t.outer_scope[3].inner_scope[3].arr.verbose vpiParameter: - LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH - vpiConstType=vpiDecConst - sub (vpiModule) t.sub + LENGTH (vpiParameter) t.outer_scope[3].inner_scope[3].arr.LENGTH vpiConstType=vpiDecConst + sub (vpiModule) t.sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.sub.subsig1 - subsig2 (vpiReg) t.sub.subsig2 + subsig1 (vpiReg) t.sub.subsig1 + subsig2 (vpiReg) t.sub.subsig2 vpiParameter: - sub_wrap (vpiModule) t.sub_wrap + sub_wrap (vpiModule) t.sub_wrap vpiDefName=ModSubWrapper vpiReg: vpiParameter: vpiInternalScope: - my_sub (vpiModule) t.sub_wrap.my_sub + my_sub (vpiModule) t.sub_wrap.my_sub vpiDefName=ModSub vpiReg: - subsig1 (vpiReg) t.sub_wrap.my_sub.subsig1 - subsig2 (vpiReg) t.sub_wrap.my_sub.subsig2 + subsig1 (vpiReg) t.sub_wrap.my_sub.subsig1 + subsig2 (vpiReg) t.sub_wrap.my_sub.subsig2 vpiParameter: *-* All Finished *-* diff --git a/test_regress/t/t_vpi_dump.v b/test_regress/t/t_vpi_dump.v index 9a4600458..c9c4b6e0f 100644 --- a/test_regress/t/t_vpi_dump.v +++ b/test_regress/t/t_vpi_dump.v @@ -65,7 +65,7 @@ module t ( /*AUTOARG*/ t_bus bus1; - sub sub (); + ModSub sub (); TestInterface intf_arr[2](); @@ -80,7 +80,7 @@ module t ( /*AUTOARG*/ genvar i; generate for (i = 1; i <= 2; i = i + 1) begin : arr - arr #(.LENGTH(i)) arr (); + ModArr #(.LENGTH(i)) arr (); end for (i = 1; i <= 3; i = i + 1) begin : outer_scope @@ -90,38 +90,38 @@ module t ( /*AUTOARG*/ genvar j; for (j = 1; j <= 3; j = j + 1) begin : inner_scope parameter int scoped_param_inner = scoped_param + 1; - arr #(.LENGTH(scoped_param_inner)) arr (); + ModArr #(.LENGTH(scoped_param_inner)) arr (); end end endgenerate - sub_wrapper sub_wrap (); + ModSubWrapper sub_wrap (); generate if (do_generate == 1) begin : cond_scope - sub scoped_sub (); + ModSub scoped_sub (); parameter int scoped_wire = 1; - sub_wrapper sub_wrap_gen (); + ModSubWrapper sub_wrap_gen (); end else begin : cond_scope_else - sub scoped_sub_else (); + ModSub scoped_sub_else (); end endgenerate endmodule : t -module sub; +module ModSub; reg subsig1; reg subsig2; `ifdef IVERILOG // stop icarus optimizing signals away wire redundant = subsig1 | subsig2; `endif -endmodule : sub +endmodule : ModSub -module arr; +module ModArr; parameter LENGTH = 1; @@ -142,9 +142,9 @@ module arr; check <= 0; end -endmodule : arr +endmodule : ModArr -module sub_wrapper; - sub my_sub (); +module ModSubWrapper; + ModSub my_sub (); endmodule diff --git a/test_regress/t/t_vpi_dump_missing_scopes.out b/test_regress/t/t_vpi_dump_missing_scopes.out index 378dc68d3..23c8997eb 100644 --- a/test_regress/t/t_vpi_dump_missing_scopes.out +++ b/test_regress/t/t_vpi_dump_missing_scopes.out @@ -1,29 +1,29 @@ -t (vpiModule) t +t (vpiModule) t vpiDefName=t vpiReg: vpiParameter: vpiInternalScope: - top_wrap_1 (vpiModule) t.top_wrap_1 + top_wrap_1 (vpiModule) t.top_wrap_1 vpiDefName=gen_wrapper vpiReg: vpiParameter: vpiInternalScope: - gen_loop[0] (vpiGenScope) t.top_wrap_1.gen_loop[0] + gen_loop[0] (vpiGenScope) t.top_wrap_1.gen_loop[0] vpiReg: vpiParameter: vpiInternalScope: - after_gen_loop (vpiModule) t.top_wrap_1.gen_loop[0].after_gen_loop + after_gen_loop (vpiModule) t.top_wrap_1.gen_loop[0].after_gen_loop vpiDefName=sub vpiReg: - subsig1 (vpiReg) t.top_wrap_1.gen_loop[0].after_gen_loop.subsig1 + subsig1 (vpiReg) t.top_wrap_1.gen_loop[0].after_gen_loop.subsig1 vpiParameter: - top_wrap_2 (vpiModule) t.top_wrap_2 + top_wrap_2 (vpiModule) t.top_wrap_2 vpiDefName=gen_wrapper vpiReg: vpiParameter: vpiInternalScope: - gen_loop[0] (vpiGenScope) t.top_wrap_2.gen_loop[0] + gen_loop[0] (vpiGenScope) t.top_wrap_2.gen_loop[0] vpiReg: vpiParameter: vpiInternalScope: - after_gen_loop (vpiModule) t.top_wrap_2.gen_loop[0].after_gen_loop + after_gen_loop (vpiModule) t.top_wrap_2.gen_loop[0].after_gen_loop vpiDefName=sub vpiReg: - subsig1 (vpiReg) t.top_wrap_2.gen_loop[0].after_gen_loop.subsig1 + subsig1 (vpiReg) t.top_wrap_2.gen_loop[0].after_gen_loop.subsig1 vpiParameter: *-* All Finished *-* diff --git a/test_regress/t/t_vpi_dump_no_inline.py b/test_regress/t/t_vpi_dump_no_inline.py new file mode 100755 index 000000000..83dfce490 --- /dev/null +++ b/test_regress/t/t_vpi_dump_no_inline.py @@ -0,0 +1,32 @@ +#!/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('simulator') +test.pli_filename = "t/t_vpi_dump.cpp" +test.golden_filename = "t/t_vpi_dump.out" +test.top_filename = "t/t_vpi_dump.v" + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + iv_flags2=["-g2005-sv"], + verilator_flags2=[ + "--exe --vpi --public-flat-rw --no-l2name --fno-inline", test.pli_filename, + test.t_dir + "/TestVpiMain.cpp" + ], + make_flags=['CPPFLAGS_ADD=-DVL_NO_LEGACY']) + +test.execute(use_libvpi=True, + expect_filename=test.golden_filename, + xrun_run_expect_filename=re.sub(r'\.out$', '.xrun.out', test.golden_filename), + iv_run_expect_filename=re.sub(r'\.out$', '.iv.out', test.golden_filename)) + +test.passes() From fa1b11252e606a2aa451755a9e27d1cd06acf5e9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 18 Jan 2025 15:25:49 -0500 Subject: [PATCH 39/93] Fix duplicate-named class variable equivalence (#5737). --- Changes | 1 + src/V3AstInlines.h | 6 ++++-- test_regress/t/t_class_eq.py | 18 ++++++++++++++++++ test_regress/t/t_class_eq.v | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 55 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_class_eq.py create mode 100644 test_regress/t/t_class_eq.v diff --git a/Changes b/Changes index 5d5ab3be9..e824230d1 100644 --- a/Changes +++ b/Changes @@ -33,6 +33,7 @@ Verilator 5.033 devel * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] * Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] * Fix matching language extension options including dots. +* Fix duplicate-named class variable equivalence (#5737). Verilator 5.032 2025-01-01 diff --git a/src/V3AstInlines.h b/src/V3AstInlines.h index 90ebccfd8..3f6b06c7e 100644 --- a/src/V3AstInlines.h +++ b/src/V3AstInlines.h @@ -188,8 +188,9 @@ bool AstVarRef::sameNode(const AstVarRef* samep) const { if (varScopep()) { return (varScopep() == samep->varScopep() && access() == samep->access()); } else { - return (selfPointer() == samep->selfPointer() && varp()->name() == samep->varp()->name() - && access() == samep->access()); + return (selfPointer() == samep->selfPointer() + && classOrPackagep() == samep->classOrPackagep() + && varp()->name() == samep->varp()->name() && access() == samep->access()); } } bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { @@ -197,6 +198,7 @@ bool AstVarRef::sameNoLvalue(AstVarRef* samep) const { return (varScopep() == samep->varScopep()); } else { return (selfPointer() == samep->selfPointer() + && classOrPackagep() == samep->classOrPackagep() && (!selfPointer().isEmpty() || !samep->selfPointer().isEmpty()) && varp()->name() == samep->varp()->name()); } diff --git a/test_regress/t/t_class_eq.py b/test_regress/t/t_class_eq.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_class_eq.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_eq.v b/test_regress/t/t_class_eq.v new file mode 100644 index 000000000..d6738ae57 --- /dev/null +++ b/test_regress/t/t_class_eq.v @@ -0,0 +1,32 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(); + +class A; + int num; + function new(int num); + this.num = num; + endfunction +endclass + +class B; + static A obj = new(2); +endclass + +class C; + static A obj = new(5); +endclass + + initial begin + #1; + $display("Bobj=%p Cobj=%p eq=%p", B::obj, C::obj, (B::obj == C::obj)); + if (B::obj == C::obj) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 4954e76b2862af1fff369fd14b896f4d7a9e44ce Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 18 Jan 2025 15:26:41 -0500 Subject: [PATCH 40/93] Commentary: Changes update --- Changes | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index e824230d1..c0188f094 100644 --- a/Changes +++ b/Changes @@ -22,7 +22,9 @@ Verilator 5.033 devel * Support `+incdir` with multiple directories. * Support side effects of form 'variable[index_function()]++'. * Improve VPI write errors (#5712). [Andrew Nolte] +* Improve `resetall support (#5728) (#5730). [Ethan Sifferman] * Fix error message when call task as a function (#3089). [Matthew Ballance] +* Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] * Fix mis-optimizing away `$urandom` (#5703). [Parker Schless] * Fix packages with `--public-depth 1` (#5708). [Andrew Nolte] @@ -32,8 +34,9 @@ Verilator 5.033 devel * Fix %p format output for real inside struct (#5713). * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] * Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] -* Fix matching language extension options including dots. +* Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732). [Andrew Nolte] * Fix duplicate-named class variable equivalence (#5737). +* Fix matching language extension options including dots. Verilator 5.032 2025-01-01 From f4a01eb4525f23ee6ad8b6a4f17535a45adcea61 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Mon, 20 Jan 2025 12:23:10 +0000 Subject: [PATCH 41/93] Fix inlined expression with assignment under LHS of NBA (#5736) (#5740) --- src/V3Delayed.cpp | 40 +++++--- test_regress/t/t_exprstmt_on_lhs_of_nba.py | 16 ++++ test_regress/t/t_exprstmt_on_lhs_of_nba.v | 104 +++++++++++++++++++++ 3 files changed, 147 insertions(+), 13 deletions(-) create mode 100755 test_regress/t/t_exprstmt_on_lhs_of_nba.py create mode 100644 test_regress/t/t_exprstmt_on_lhs_of_nba.v diff --git a/src/V3Delayed.cpp b/src/V3Delayed.cpp index 6b885b1d9..92b383642 100644 --- a/src/V3Delayed.cpp +++ b/src/V3Delayed.cpp @@ -208,6 +208,7 @@ class DelayedVisitor final : public VNVisitor { // NODE STATE // AstVar::user1() -> bool. Set true if already issued MULTIDRIVEN warning // AstVarRef::user1() -> bool. Set true if target of NBA + // AstAssignDly::user1() -> bool. Set true if already visited // AstNodeModule::user1p() -> std::unorded_map temp map via m_varMap // AstVarScope::user1p() -> VarScopeInfo via m_vscpInfo // AstVarScope::user2p() -> AstVarRef*: First write reference to the Variable @@ -909,6 +910,9 @@ class DelayedVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); } void visit(AstAssignDly* nodep) override { + // Prevent double processing due to AstExprStmt being moved before this node + if (nodep->user1SetOnce()) return; + if (m_cfuncp) { if (!v3Global.rootp()->nbaEventp()) { nodep->v3warn( @@ -922,21 +926,31 @@ class DelayedVisitor final : public VNVisitor { UASSERT_OBJ(m_inSuspendableOrFork || m_activep->hasClocked(), nodep, "<= assignment in non-clocked block, should have been converted in V3Active"); - // Grab the reference to the target of the NBA + // Grab the reference to the target of the NBA, also lift ExprStmt statements on the LHS VL_RESTORER(m_currNbaLhsRefp); UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "NBAs should not nest"); - nodep->lhsp()->foreach([&](AstVarRef* nodep) { - // Ignore reads (e.g.: '_[*here*] <= _') - if (nodep->access().isReadOnly()) return; - // A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at this - // point. These should be lowered in an earlier pass into sequenced temporaries. - UASSERT_OBJ(!nodep->access().isRW(), nodep, "RW ref on LHS of NBA"); - // Multiple target variables - // (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _'). - // These should be lowered in an earlier pass into sequenced statements. - UASSERT_OBJ(!m_currNbaLhsRefp, nodep, "Multiple Write refs on LHS of NBA"); - // Hold on to it - m_currNbaLhsRefp = nodep; + nodep->lhsp()->foreach([&](AstNode* currp) { + if (AstExprStmt* const exprp = VN_CAST(currp, ExprStmt)) { + // Move statements before the NBA + nodep->addHereThisAsNext(exprp->stmtsp()->unlinkFrBackWithNext()); + // Replace with result + currp->replaceWith(exprp->resultp()->unlinkFrBack()); + // Get rid of the AstExprStmt + VL_DO_DANGLING(pushDeletep(currp), currp); + } else if (AstVarRef* const refp = VN_CAST(currp, VarRef)) { + // Ignore reads (e.g.: '_[*here*] <= _') + if (refp->access().isReadOnly()) return; + // A RW ref on the LHS (e.g.: '_[preInc(*here*)] <= _') is asking for trouble at + // this point. These should be lowered in an earlier pass into sequenced + // temporaries. + UASSERT_OBJ(!refp->access().isRW(), refp, "RW ref on LHS of NBA"); + // Multiple target variables + // (e.g.: '{*here*, *and here*} <= _',or '*here*[*and here* = _] <= _'). + // These should be lowered in an earlier pass into sequenced statements. + UASSERT_OBJ(!m_currNbaLhsRefp, refp, "Multiple Write refs on LHS of NBA"); + // Hold on to it + m_currNbaLhsRefp = refp; + } }); // The target variable of the NBA (there can only be one per NBA at this point) AstVarScope* const vscp = m_currNbaLhsRefp->varScopep(); diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.py b/test_regress/t/t_exprstmt_on_lhs_of_nba.py new file mode 100755 index 000000000..319c0ff4a --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +test.compile() + +test.passes() diff --git a/test_regress/t/t_exprstmt_on_lhs_of_nba.v b/test_regress/t/t_exprstmt_on_lhs_of_nba.v new file mode 100644 index 000000000..e9c7efb4e --- /dev/null +++ b/test_regress/t/t_exprstmt_on_lhs_of_nba.v @@ -0,0 +1,104 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Outputs + data_o, + // Inputs + clk, rst_i, write_valid_i, write_front_i, read_valid_i, data_i + ); + + localparam NR_ELEMENTS = 16; + localparam DATAW = 32; + + input clk; + input rst_i; + input write_valid_i; + input write_front_i; + input read_valid_i; + input [31:0] data_i; + output [31:0] data_o; + + reg [31:0] FIFOContent [NR_ELEMENTS-1:0]; + + typedef logic [$clog2(NR_ELEMENTS)-1:0] FIFOPointer_t; + + // verilator lint_off WIDTH + localparam FIFOPointer_t MAX_PTR_VAL = NR_ELEMENTS-1; + // verilator lint_on WIDTH + localparam FIFOPointer_t MIN_PTR_VAL = 0; + localparam FIFOPointer_t PTR_INC = 1; + FIFOPointer_t write_pointer; + FIFOPointer_t read_pointer; + + function FIFOPointer_t nextPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MAX_PTR_VAL) + nextPointer = MIN_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + nextPointer = val + PTR_INC; + endfunction + + function FIFOPointer_t prevPointer(input FIFOPointer_t val); + if ($clog2(NR_ELEMENTS) == $clog2(NR_ELEMENTS+1) + && val == MIN_PTR_VAL) + prevPointer = MAX_PTR_VAL; // explicit wrap if NR_ELEMENTS is not a power of 2 + else + prevPointer = val - PTR_INC; + endfunction + + reg [$clog2(NR_ELEMENTS)-1:0] level; + reg is_empty; + + always @(posedge clk) begin + if (write_valid_i) + FIFOContent[write_front_i ? (read_valid_i ? read_pointer : prevPointer(read_pointer)) : write_pointer] <= data_i; + end + + assign data_o = FIFOContent[read_pointer]; + + always @(posedge clk) begin + if (rst_i) begin + is_empty <= 1; + end + else if (write_valid_i) begin + is_empty <= 0; + end + else if (read_valid_i && write_pointer == nextPointer(read_pointer)) begin + is_empty <= 1; + end + end + + always @(posedge clk) begin + if (rst_i) begin + level <= 0; + end + else begin + level <= level + (write_valid_i ? 1 : 0) - (read_valid_i ? 1 : 0); + end + end + + always @(posedge clk) begin + if (rst_i) begin + write_pointer <= 0; + end + else if (write_valid_i && !write_front_i) begin + write_pointer <= nextPointer(write_pointer); + end + end + + always @(posedge clk) begin + if (rst_i) begin + read_pointer <= 0; + end + else if (read_valid_i) begin + if (!(write_valid_i && write_front_i))read_pointer <= nextPointer(read_pointer); + end + else if (write_valid_i && write_front_i) begin + read_pointer <= prevPointer(read_pointer); + end + end +endmodule From 0507fb46558b1f0826e3feea2b3b5113db6a2d89 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Mon, 20 Jan 2025 20:24:09 +0100 Subject: [PATCH 42/93] Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734) --- docs/guide/exe_verilator.rst | 7 + src/V3AstNodeOther.h | 7 +- src/V3Config.cpp | 26 ++- src/V3Config.h | 4 + src/V3ExecGraph.cpp | 8 +- src/V3OrderParallel.cpp | 6 +- src/V3ProtectLib.cpp | 30 +++ src/V3Task.cpp | 15 +- src/verilog.l | 1 + src/verilog.y | 7 + test_regress/t/t_hier_block_perf.py | 40 ++++ test_regress/t/t_hier_block_perf.v | 314 ++++++++++++++++++++++++++++ 12 files changed, 456 insertions(+), 9 deletions(-) create mode 100755 test_regress/t/t_hier_block_perf.py create mode 100644 test_regress/t/t_hier_block_perf.v diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index d9b4075ab..03f8a2454 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -2154,6 +2154,13 @@ The grammar of configuration commands is as follows: :option:`/*verilator&32;public_flat*/`, etc., metacomments. See also :ref:`VPI Example`. +.. option:: profile_data -hier-dpi "" -cost + + Internal profiling data inserted during :vlopt:`--hierarchical`; specifies + execution cost of a hierarchical DPI wrappers for modules with + :option:`/*verilator&32;hier_block*/` metacomment. See + :ref:`Hierarchical Verilation`. + .. option:: profile_data -mtask "" -cost Feeds profile-guided optimization data into the Verilator algorithms in diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 811850d71..e8b8c425f 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -643,6 +643,7 @@ class AstCFunc final : public AstNode { bool m_dpiImportWrapper : 1; // Wrapper for invoking DPI import prototype from generated code bool m_needProcess : 1; // Needs access to VlProcess of the caller bool m_recursive : 1; // Recursive or part of recursion + int m_cost; // Function call cost public: AstCFunc(FileLine* fl, const string& name, AstScope* scopep, const string& rtnType = "") : ASTGEN_SUPER_CFunc(fl) { @@ -671,6 +672,7 @@ public: m_dpiImportPrototype = false; m_dpiImportWrapper = false; m_recursive = false; + m_cost = v3Global.opt.instrCountDpi(); // As proxy for unknown general DPI cost } ASTGEN_MEMBERS_AstCFunc; string name() const override VL_MT_STABLE { return m_name; } @@ -685,9 +687,7 @@ public: } // void name(const string& name) override { m_name = name; } - int instrCount() const override { - return dpiImportPrototype() ? v3Global.opt.instrCountDpi() : 0; - } + int instrCount() const override { return m_cost; } VBoolOrUnknown isConst() const { return m_isConst; } void isConst(bool flag) { m_isConst.setTrueOrFalse(flag); } void isConst(VBoolOrUnknown flag) { m_isConst = flag; } @@ -746,6 +746,7 @@ public: bool isCoroutine() const { return m_rtnType == "VlCoroutine"; } void recursive(bool flag) { m_recursive = flag; } bool recursive() const { return m_recursive; } + void cost(int cost) { m_cost = cost; } // Special methods bool emptyBody() const { return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr diff --git a/src/V3Config.cpp b/src/V3Config.cpp index 9b8d38a61..f637cacd3 100644 --- a/src/V3Config.cpp +++ b/src/V3Config.cpp @@ -533,11 +533,13 @@ public: // Resolve modules and files in the design class V3ConfigResolver final { + enum ProfileDataMode : uint8_t { NONE = 0, MTASK = 1, HIER_DPI = 2 }; V3ConfigModuleResolver m_modules; // Access to module names (with wildcards) V3ConfigFileResolver m_files; // Access to file names (with wildcards) V3ConfigScopeTraceResolver m_scopeTraces; // Regexp to trace enables std::unordered_map> m_profileData; // Access to profile_data records + uint8_t m_mode = NONE; FileLine* m_profileFileLine = nullptr; V3ConfigResolver() = default; @@ -552,10 +554,21 @@ public: V3ConfigFileResolver& files() { return m_files; } V3ConfigScopeTraceResolver& scopeTraces() { return m_scopeTraces; } - void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) { + void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { + // Empty key for hierarchical DPI wrapper costs. + addProfileData(fl, hierDpi, "", cost, HIER_DPI); + } + void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost, + ProfileDataMode mode = MTASK) { if (!m_profileFileLine) m_profileFileLine = fl; if (cost == 0) cost = 1; // Cost 0 means delete (or no data) m_profileData[model][key] += cost; + m_mode |= mode; + } + bool containsMTaskProfileData() const { return m_mode & MTASK; } + uint64_t getProfileData(const string& hierDpi) const { + // Empty key for hierarchical DPI wrapper costs. + return getProfileData(hierDpi, ""); } uint64_t getProfileData(const string& model, const string& key) const { const auto mit = m_profileData.find(model); @@ -619,6 +632,10 @@ void V3Config::addModulePragma(const string& module, VPragmaType pragma) { V3ConfigResolver::s().modules().at(module).addModulePragma(pragma); } +void V3Config::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { + V3ConfigResolver::s().addProfileData(fl, hierDpi, cost); +} + void V3Config::addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost) { V3ConfigResolver::s().addProfileData(fl, model, key, cost); @@ -724,6 +741,9 @@ void V3Config::applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar if (vp) vp->apply(varp); } +uint64_t V3Config::getProfileData(const string& hierDpi) { + return V3ConfigResolver::s().getProfileData(hierDpi); +} uint64_t V3Config::getProfileData(const string& model, const string& key) { return V3ConfigResolver::s().getProfileData(model, key); } @@ -736,6 +756,10 @@ bool V3Config::getScopeTraceOn(const string& scope) { void V3Config::contentsPushText(const string& text) { return WildcardContents::pushText(text); } +bool V3Config::containsMTaskProfileData() { + return V3ConfigResolver::s().containsMTaskProfileData(); +} + bool V3Config::waive(FileLine* filelinep, V3ErrorCode code, const string& message) { V3ConfigFile* filep = V3ConfigResolver::s().files().resolve(filelinep->filename()); if (!filep) return false; diff --git a/src/V3Config.h b/src/V3Config.h index 32ee079c0..df7287c07 100644 --- a/src/V3Config.h +++ b/src/V3Config.h @@ -38,6 +38,7 @@ public: const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); static void addModulePragma(const string& module, VPragmaType pragma); + static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, uint64_t cost); static void addScopeTraceOn(bool on, const string& scope, int levels); @@ -51,12 +52,15 @@ public: static void applyModule(AstNodeModule* modulep); static void applyVarAttr(AstNodeModule* modulep, AstNodeFTask* ftaskp, AstVar* varp); + static uint64_t getProfileData(const string& hierDpi); static uint64_t getProfileData(const string& model, const string& key); static FileLine* getProfileDataFileLine(); static bool getScopeTraceOn(const string& scope); static void contentsPushText(const string& text); + static bool containsMTaskProfileData(); + static bool waive(FileLine* filelinep, V3ErrorCode code, const string& message); }; diff --git a/src/V3ExecGraph.cpp b/src/V3ExecGraph.cpp index 5ccc71da5..49c890034 100644 --- a/src/V3ExecGraph.cpp +++ b/src/V3ExecGraph.cpp @@ -528,9 +528,11 @@ void fillinCosts(V3Graph* execMTaskGraphp) { if (missingProfiles) { if (FileLine* const fl = V3Config::getProfileDataFileLine()) { - fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. " - << missingProfiles << " of " << totalEstimates - << " mtasks had no data"); + if (V3Config::containsMTaskProfileData()) { + fl->v3warn(PROFOUTOFDATE, "Profile data for mtasks may be out of date. " + << missingProfiles << " of " << totalEstimates + << " mtasks had no data"); + } } } } diff --git a/src/V3OrderParallel.cpp b/src/V3OrderParallel.cpp index 67fe62bd3..3c410d1bb 100644 --- a/src/V3OrderParallel.cpp +++ b/src/V3OrderParallel.cpp @@ -1710,7 +1710,11 @@ class DpiImportCallVisitor final : public VNVisitor { if (nodep->dpiImportWrapper()) { if (nodep->dpiPure() ? !v3Global.opt.threadsDpiPure() : !v3Global.opt.threadsDpiUnpure()) { - m_hasDpiHazard = true; + // If hierarchical DPI wrapper cost is not found or is of a 0 cost, + // we have a normal DPI which induces DPI hazard by default. + m_hasDpiHazard = V3Config::getProfileData(nodep->cname()) == 0; + UINFO(9, "DPI wrapper '" << nodep->cname() + << "' has dpi hazard = " << m_hasDpiHazard << endl); } } iterateChildren(nodep); diff --git a/src/V3ProtectLib.cpp b/src/V3ProtectLib.cpp index ec66dba2d..7ca088113 100644 --- a/src/V3ProtectLib.cpp +++ b/src/V3ProtectLib.cpp @@ -19,6 +19,7 @@ #include "V3ProtectLib.h" #include "V3Hasher.h" +#include "V3InstrCount.h" #include "V3String.h" #include "V3Task.h" @@ -98,6 +99,32 @@ class ProtectVisitor final : public VNVisitor { txtp->addNodesp(new AstComment{fl, comment}); } + void configSection(AstNodeModule* modp, AstTextBlock* txtp, FileLine* fl) { + txtp->addText(fl, "\n`ifdef VERILATOR\n"); + txtp->addText(fl, "`verilator_config\n"); + + // The `eval` function is called inside both update functions. As those functions + // are created by text bashing, we need to find cost of `_eval` which is the first function + // with a real cost in AST. + uint32_t cost = 0; + modp->foreach([&cost](AstCFunc* cfuncp) { + if (cfuncp->name() == "_eval") cost = V3InstrCount::count(cfuncp, false); + }); + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_combo_update\" -cost 64'd" + std::to_string(cost) + + "\n"); + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_seq_update\" -cost 64'd" + std::to_string(cost) + + "\n"); + + // Mark remaining NDA protectlib wrapper DPIs as non-hazardous by deliberately forwarding + // them with non-zero cost. + txtp->addText(fl, "profile_data -hier-dpi \"" + m_libName + + "_protectlib_combo_ignore\" -cost 64'd1\n"); + txtp->addText(fl, "`verilog\n"); + txtp->addText(fl, "`endif\n"); + } + void hashComment(AstTextBlock* txtp, FileLine* fl) { addComment(txtp, fl, "Checks to make sure the .sv wrapper and library agree"); } @@ -283,6 +310,9 @@ class ProtectVisitor final : public VNVisitor { txtp->addText(fl, "final " + m_libName + "_protectlib_final(handle__V);\n\n"); txtp->addText(fl, "endmodule\n"); + + configSection(modp, txtp, fl); + m_vfilep->tblockp(txtp); } diff --git a/src/V3Task.cpp b/src/V3Task.cpp index 0fe3624d3..eb8716133 100644 --- a/src/V3Task.cpp +++ b/src/V3Task.cpp @@ -27,6 +27,7 @@ #include "V3Task.h" +#include "V3Config.h" #include "V3Const.h" #include "V3EmitCBase.h" #include "V3Graph.h" @@ -372,6 +373,7 @@ class TaskVisitor final : public VNVisitor { // STATE - across all visitors DpiCFuncs m_dpiNames; // Map of all created DPI functions VDouble0 m_statInlines; // Statistic tracking + VDouble0 m_statHierDpisWithCosts; // Statistic tracking // METHODS @@ -980,6 +982,11 @@ class TaskVisitor final : public VNVisitor { funcp->isMethod(false); funcp->protect(false); funcp->dpiPure(nodep->dpiPure()); + + const int cost = static_cast(V3Config::getProfileData(funcp->name())); + m_statHierDpisWithCosts += (cost != 0); + funcp->cost(cost); + // Add DPI Import to top, since it's a global function m_topScopep->scopep()->addBlocksp(funcp); makePortList(nodep, funcp); @@ -1259,6 +1266,8 @@ class TaskVisitor final : public VNVisitor { if (nodep->name() == "new") cfuncp->isConstructor(true); if (cfuncp->dpiExportImpl()) cfuncp->cname(nodep->cname()); + if (cfuncp->dpiImportWrapper()) cfuncp->cname(nodep->cname()); + if (!nodep->dpiImport() && !nodep->taskPublic()) { // Need symbol table cfuncp->argTypes(EmitCBase::symClassVar()); @@ -1613,7 +1622,11 @@ public: : m_statep{statep} { iterate(nodep); } - ~TaskVisitor() { V3Stats::addStat("Optimizations, Functions inlined", m_statInlines); } + ~TaskVisitor() { + V3Stats::addStat("Optimizations, Functions inlined", m_statInlines); + V3Stats::addStat("Optimizations, Hierarchical DPI wrappers with costs", + m_statHierDpisWithCosts); + } }; //###################################################################### diff --git a/src/verilog.l b/src/verilog.l index 5aa52e4ed..7e96bdf44 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -141,6 +141,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} -?"-cost" { FL; return yVLT_D_COST; } -?"-file" { FL; return yVLT_D_FILE; } -?"-function" { FL; return yVLT_D_FUNCTION; } + -?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; } -?"-levels" { FL; return yVLT_D_LEVELS; } -?"-lines" { FL; return yVLT_D_LINES; } -?"-match" { FL; return yVLT_D_MATCH; } diff --git a/src/verilog.y b/src/verilog.y index 8e6a48ea7..3d66945f0 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -492,6 +492,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_D_COST "--cost" %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" +%token yVLT_D_HIER_DPI "--hier-dpi" %token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" @@ -7655,6 +7656,8 @@ vltItem: { V3Config::addCaseParallel(*$2, 0); } | yVLT_PARALLEL_CASE vltDFile yVLT_D_LINES yaINTNUM { V3Config::addCaseParallel(*$2, $4->toUInt()); } + | yVLT_PROFILE_DATA vltDHierDpi vltDCost + { V3Config::addProfileData($1, *$2, $3->toUQuad()); } | yVLT_PROFILE_DATA vltDModel vltDMtask vltDCost { V3Config::addProfileData($1, *$2, *$3, $4->toUQuad()); } ; @@ -7699,6 +7702,10 @@ vltDFile: // --file yVLT_D_FILE str { $$ = $2; } ; +vltDHierDpi: // --hier-dpi + yVLT_D_HIER_DPI str { $$ = $2; } + ; + vltDLevels: // --levels yVLT_D_LEVELS yaINTNUM { $$ = $2; } ; diff --git a/test_regress/t/t_hier_block_perf.py b/test_regress/t/t_hier_block_perf.py new file mode 100755 index 000000000..f73b5db4c --- /dev/null +++ b/test_regress/t/t_hier_block_perf.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt_all') +test.init_benchmarksim() +test.cycles = (int(test.benchmark) if test.benchmark else 1000000) +test.sim_time = test.cycles * 10 + 1000 +THREADS = int(os.environ["SIM_THREADS"]) if "SIM_THREADS" in os.environ else 2 + +test.compile(benchmarksim=1, + v_flags2=[ + "+define+SIM_CYCLES=" + str(test.cycles), "--prof-exec", "--hierarchical", + "--stats" + ], + threads=(THREADS if test.vltmt else 1)) + +test.file_grep(test.obj_dir + "/V" + test.name + "__hier.dir/V" + test.name + "__stats.txt", + r'Optimizations, Hierarchical DPI wrappers with costs\s+(\d+)', 3) + +test.execute(all_run_flags=[ + "+verilator+prof+exec+start+2", + " +verilator+prof+exec+window+2", + " +verilator+prof+exec+file+" + test.obj_dir + "/profile_exec.dat", + " +verilator+prof+vlt+file+" + test.obj_dir + "/profile.vlt"]) # yapf:disable + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_gantt", test.obj_dir + + "/profile_exec.dat", "--vcd " + test.obj_dir + "/profile_exec.vcd", "| tee " + test.obj_dir + + "/gantt.log" +]) + +test.passes() diff --git a/test_regress/t/t_hier_block_perf.v b/test_regress/t/t_hier_block_perf.v new file mode 100644 index 000000000..59470bb03 --- /dev/null +++ b/test_regress/t/t_hier_block_perf.v @@ -0,0 +1,314 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// based on t_gate_ormux + +`ifndef HIER_CORES + `define HIER_CORES 3 +`endif + +`ifndef MAIN_CORES + `define MAIN_CORES 1 +`endif + +module t (/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + generate + for (genvar i = 0; i < `MAIN_CORES; ++i) NonHierCore mainCore(clk); + endgenerate + + generate + for (genvar i = 0; i < `HIER_CORES; ++i) Core hierCore(clk); + endgenerate +endmodule + +module Core(input clk); /* verilator hier_block */ + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module NonHierCore(input clk); + reg [63:0] crc; + logic [31:0] rdata; + logic [31:0] rdata2; + wire [31:0] wdata = crc[31:0]; + wire [15:0] sel = {11'h0, crc[36:32]}; + wire we = crc[48]; + + Test test ( + // Outputs + .rdata (rdata[31:0]), + .rdata2 (rdata2[31:0]), + // Inputs + .clk (clk), + .we (we), + .sel (sel[15:0]), + .wdata (wdata[31:0])); + wire [63:0] result = {rdata2, rdata}; + + Check check(.clk(clk), .crc(crc), .result(result), .rdata(rdata), .rdata2(rdata2)); +endmodule + +module Check( + input clk, + output reg [63:0] crc, + input wire [63:0] result, + input logic [31:0] rdata, + input logic [31:0] rdata2 + ); + integer cyc = 0; + reg [63:0] sum; + + always @ (posedge clk) begin +`ifdef TEST_VERBOSE + $write("[%0t] cyc==%0d crc=%x result=%x\n", $time, cyc, crc, result); +`endif + cyc <= cyc + 1; + crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]}; + sum <= result ^ {sum[62:0], sum[63] ^ sum[2] ^ sum[0]}; + if (rdata2 != rdata) $stop; + if (cyc==0) begin + // Setup + crc <= 64'h5aef0c8d_d70a4497; + sum <= '0; + end + else if (cyc<10) begin + sum <= '0; + end + else if (cyc == 99) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + if (crc !== 64'hc77bb9b3784ea091) $stop; +`define EXPECTED_SUM 64'h8977713eb467bc86 + if (sum !== `EXPECTED_SUM) $stop; + end + else if (cyc == `SIM_CYCLES) begin + $write("[%0t] cyc==%0d crc=%x sum=%x\n", $time, cyc, crc, sum); + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule + +module Test(/*AUTOARG*/ + // Outputs + rdata, rdata2, + // Inputs + clk, we, sel, wdata + ); + input clk; + input we; + input [15:0] sel; + input [31:0] wdata; + output logic [31:0] rdata; + output logic [31:0] rdata2; + + logic we_d1r; + logic [15:0] sel_d1r; + logic [31:0] wdata_d1r; + always_ff @ (posedge clk) begin + we_d1r <= we; + sel_d1r <= sel; + wdata_d1r <= wdata; + end + + reg [31:0] csr0000; + reg [31:0] csr0001; + reg [31:0] csr0002; + reg [31:0] csr0003; + reg [31:0] csr0004; + reg [31:0] csr0005; + reg [31:0] csr0006; + reg [31:0] csr0007; + reg [31:0] csr0008; + reg [31:0] csr0009; + reg [31:0] csr000a; + reg [31:0] csr000b; + reg [31:0] csr000c; + reg [31:0] csr000d; + reg [31:0] csr000e; + reg [31:0] csr000f; + wire [31:0] csr0010 = 32'h33675230; + wire [31:0] csr0011 = 32'h00fa2144; + wire [31:0] csr0012 = 32'h6a5e8e10; + wire [31:0] csr0013 = 32'h000a5b5e; + wire [31:0] csr0014 = 32'h002fe51b; + wire [31:0] csr0015 = 32'h00027e00; + wire [31:0] csr0016 = 32'h0000e3c0; + wire [31:0] csr0017 = 32'h00efcf16; + wire [31:0] csr0018 = 32'h007a2600; + wire [31:0] csr0019 = 32'h0a4a9f10; + wire [31:0] csr001a = 32'h7d789de3; + wire [31:0] csr001b = 32'h40f655f9; + wire [31:0] csr001c = 32'hadad01f4; + wire [31:0] csr001d = 32'h02e7b33c; + wire [31:0] csr001e = 32'h12101533; + wire [31:0] csr001f = 32'h2cc1cce5; + initial begin + csr0000 = 32'he172d365; + csr0001 = 32'h35cc25e2; + csr0002 = 32'haf48436e; + csr0003 = 32'h135e55e4; + csr0004 = 32'h5fd6e48a; + csr0005 = 32'hb07d34ad; + csr0006 = 32'h2aa05deb; + csr0007 = 32'hfe97b680; + csr0008 = 32'h960f20bb; + csr0009 = 32'h251129f0; + csr000a = 32'hef3d2f93; + csr000b = 32'hef4bc127; + csr000c = 32'h3dfecb10; + csr000d = 32'h1b4690f5; + csr000e = 32'ha07822ab; + csr000f = 32'hf817cbf6; + end + + always_ff @ (posedge clk) begin + if (we_d1r && sel_d1r == 16'h0000) csr0000 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0001) csr0001 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0002) csr0002 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0003) csr0003 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0004) csr0004 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0005) csr0005 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0006) csr0006 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0007) csr0007 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0008) csr0008 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h0009) csr0009 <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000a) csr000a <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000b) csr000b <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000c) csr000c <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000d) csr000d <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000e) csr000e <= wdata_d1r; + if (we_d1r && sel_d1r == 16'h000f) csr000f <= wdata_d1r; + end + + wire dec0000 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0001 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0002 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0003 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0004 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0005 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0006 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0007 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0008 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0009 = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000a = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000b = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec000c = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec000d = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec000e = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec000f = sel_d1r[15:6] == 0 && !sel_d1r[5] && !sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0010 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0011 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0012 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0013 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0014 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0015 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec0016 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec0017 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && !sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec0018 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec0019 = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001a = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001b = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && !sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + wire dec001c = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && !sel_d1r[0]; + wire dec001d = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && !sel_d1r[1] && sel_d1r[0]; + wire dec001e = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && !sel_d1r[0]; + wire dec001f = sel_d1r[15:6] == 0 && !sel_d1r[5] && sel_d1r[4] && sel_d1r[3] && sel_d1r[2] && sel_d1r[1] && sel_d1r[0]; + + assign rdata = (32'h0 + | {32{dec0000}} & csr0000 + | {32{dec0001}} & csr0001 + | {32{dec0002}} & csr0002 + | {32{dec0003}} & csr0003 + | {32{dec0004}} & csr0004 + | {32{dec0005}} & csr0005 + | {32{dec0006}} & csr0006 + | {32{dec0007}} & csr0007 + | {32{dec0008}} & csr0008 + | {32{dec0009}} & csr0009 + | {32{dec000a}} & csr000a + | {32{dec000b}} & csr000b + | {32{dec000c}} & csr000c + | {32{dec000d}} & csr000d + | {32{dec000e}} & csr000e + | {32{dec000f}} & csr000f + | {32{dec0010}} & csr0010 + | {32{dec0011}} & csr0011 + | {32{dec0012}} & csr0012 + | {32{dec0013}} & csr0013 + | {32{dec0014}} & csr0014 + | {32{dec0015}} & csr0015 + | {32{dec0016}} & csr0016 + | {32{dec0017}} & csr0017 + | {32{dec0018}} & csr0018 + | {32{dec0019}} & csr0019 + | {32{dec001a}} & csr001a + | {32{dec001b}} & csr001b + | {32{dec001c}} & csr001c + | {32{dec001d}} & csr001d + | {32{dec001e}} & csr001e + | {32{dec001f}} & csr001f + ); + + always_comb begin + case (sel_d1r) + 16'h0000: rdata2 = csr0000; + 16'h0001: rdata2 = csr0001; + 16'h0002: rdata2 = csr0002; + 16'h0003: rdata2 = csr0003; + 16'h0004: rdata2 = csr0004; + 16'h0005: rdata2 = csr0005; + 16'h0006: rdata2 = csr0006; + 16'h0007: rdata2 = csr0007; + 16'h0008: rdata2 = csr0008; + 16'h0009: rdata2 = csr0009; + 16'h000a: rdata2 = csr000a; + 16'h000b: rdata2 = csr000b; + 16'h000c: rdata2 = csr000c; + 16'h000d: rdata2 = csr000d; + 16'h000e: rdata2 = csr000e; + 16'h000f: rdata2 = csr000f; + 16'h0010: rdata2 = csr0010; + 16'h0011: rdata2 = csr0011; + 16'h0012: rdata2 = csr0012; + 16'h0013: rdata2 = csr0013; + 16'h0014: rdata2 = csr0014; + 16'h0015: rdata2 = csr0015; + 16'h0016: rdata2 = csr0016; + 16'h0017: rdata2 = csr0017; + 16'h0018: rdata2 = csr0018; + 16'h0019: rdata2 = csr0019; + 16'h001a: rdata2 = csr001a; + 16'h001b: rdata2 = csr001b; + 16'h001c: rdata2 = csr001c; + 16'h001d: rdata2 = csr001d; + 16'h001e: rdata2 = csr001e; + 16'h001f: rdata2 = csr001f; + default: rdata2 = 0; + endcase + end + +endmodule From 35fa0d051d254187bcace0c5d076d53817a9c47d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Fri, 24 Jan 2025 13:47:11 +0100 Subject: [PATCH 43/93] Support integer atom type ports in `--hierarchical` (#5748) --- src/V3EmitV.cpp | 3 + test_regress/t/t_debug_emitv.out | 94 +++++++++++++++--------------- test_regress/t/t_hier_block_int.py | 21 +++++++ test_regress/t/t_hier_block_int.v | 50 ++++++++++++++++ 4 files changed, 121 insertions(+), 47 deletions(-) create mode 100755 test_regress/t/t_hier_block_int.py create mode 100644 test_regress/t/t_hier_block_int.v diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index b5da1b091..c61d3c0cc 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -611,6 +611,9 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstBasicDType* nodep) override { putfs(nodep, nodep->prettyName()); if (nodep->isSigned()) putfs(nodep, " signed"); + // Do not emit ranges for integer atoms. + if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return; + if (nodep->rangep()) { puts(" "); iterateAndNextConstNull(nodep->rangep()); diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 20e344b0f..4e2b89ac7 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -7,70 +7,70 @@ module Vt_debug_emitv_t; ???? // ENUMITEM 'ZERO' 32'h0 ???? // ENUMITEM 'ONE' - 32'h1int signed [31:0] struct packed - {int signed [31:0] a;}logic signed [2:0] struct + 32'h1int signedstruct packed + {int signed a;}logic signed [2:0] struct {logic signed [2:0] a;}logicunion {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct + {int signed a;}bit [31:0] const struct packed + {int signed a;}const struct packed + {int signed a;}[0:2]struct {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] + {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed ???? // QUEUEDTYPE - int signed [31:0] string + int signedstring ???? // ASSOCARRAYDTYPE - int signed [31:0] + int signed ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] e_t; + int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed e_t; typedef struct packed - {int signed [31:0] a;}logic signed [2:0] struct + {int signed a;}logic signed [2:0] struct {logic signed [2:0] a;}logicunion {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct + {int signed a;}bit [31:0] const struct packed + {int signed a;}const struct packed + {int signed a;}[0:2]struct {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] + {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed ???? // QUEUEDTYPE - int signed [31:0] string + int signedstring ???? // ASSOCARRAYDTYPE - int signed [31:0] + int signed ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] ps_t; + int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed ps_t; typedef struct {logic signed [2:0] a;}logicunion {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct + {int signed a;}bit [31:0] const struct packed + {int signed a;}const struct packed + {int signed a;}[0:2]struct {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] + {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed ???? // QUEUEDTYPE - int signed [31:0] string + int signedstring ???? // ASSOCARRAYDTYPE - int signed [31:0] + int signed ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] us_t; + int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed us_t; typedef union {logic a;}struct packed - {int signed [31:0] a;}bit [31:0] const struct packed - {int signed [31:0] a;}const struct packed - {int signed [31:0] a;}[0:2]struct + {int signed a;}bit [31:0] const struct packed + {int signed a;}const struct packed + {int signed a;}[0:2]struct {logic signed [2:0] a;}union - {logic a;}int signed [31:0] int signed [31:0] [0:2]logic [15:0] logic [15:0] logic [15:0] int signed [31:0] int signed [31:0] int signed [31:0] + {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed ???? // QUEUEDTYPE - int signed [31:0] string + int signedstring ???? // ASSOCARRAYDTYPE - int signed [31:0] + int signed ???? // DYNARRAYDTYPE - int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] int signed [31:0] real signedstringIData [31:0] logic signed [31:0] int signed [31:0] union_t; + int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed union_t; struct packed - {int signed [31:0] a;} ps[0:2]; + {int signed a;} ps[0:2]; struct {logic signed [2:0] a;} us; union {logic a;} unu; - int signed [31:0] array[0:2]; + int signed array[0:2]; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end @@ -78,8 +78,8 @@ module Vt_debug_emitv_t; logic [15:0] pubflat_r; logic [15:0] pubflat_w; assign pubflat_w = pubflat; - int signed [31:0] fd; - int signed [31:0] i; + int signed fd; + int signed i; ???? // QUEUEDTYPE q; @@ -93,7 +93,7 @@ module Vt_debug_emitv_t; $display("stmt"); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 $display("stmt"); f = ((v == 'sh0) ? 'sh63 : ((~ v) + 'sh1)); @@ -102,10 +102,10 @@ module Vt_debug_emitv_t; endfunction initial begin begin : unnamedblk1 - int signed [31:0] other; + int signed other; begin begin : unnamedblk2 - int signed [31:0] i; + int signed i; i = 'sh0; while ((i < 'sh3)) begin begin @@ -144,9 +144,9 @@ module Vt_debug_emitv_t; $display("negedge clk, pfr = %x", pubflat_r); end end - int signed [31:0] cyc; - int signed [31:0] fo; - int signed [31:0] sum; + int signed cyc; + int signed fo; + int signed sum; real signed r; string str; always @(posedge clk) begin @@ -177,7 +177,7 @@ module Vt_debug_emitv_t; $readmemh(fd, array, 'sh0, 'sh0); sum = 'sh0; begin : unnamedblk3 - int signed [31:0] i; + int signed i; i = 'sh0; begin : label0 while ((i < cyc)) begin @@ -298,7 +298,7 @@ module Vt_debug_emitv_t; $display("%g", $atanh(r)); force sum = 'sha; begin : unnamedblk1_1 - integer signed [31:0] __Vrepeat0; + integer signed __Vrepeat0; __Vrepeat0 = 'sh2; while ((__Vrepeat0 > 32'h0)) begin if ((sum != 'sha)) begin @@ -314,7 +314,7 @@ module Vt_debug_emitv_t; endmodule package Vt_debug_emitv___024unit; class Vt_debug_emitv_Cls; - int signed [31:0] member; + int signed member; member = 'sh1; task method; endtask @@ -324,12 +324,12 @@ package Vt_debug_emitv___024unit; endpackage module Vt_debug_emitv_sub; task inc; - input int signed [31:0] i; - output int signed [31:0] o; + input int signed i; + output int signed o; o = ({32'h1{{1'h0, i[31:1]}}} + 32'h1); endtask function f; - input int signed [31:0] v; + input int signed v; begin : label0 if ((v == 'sh0)) begin f = 'sh21; diff --git a/test_regress/t/t_hier_block_int.py b/test_regress/t/t_hier_block_int.py new file mode 100755 index 000000000..0384eccd5 --- /dev/null +++ b/test_regress/t/t_hier_block_int.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--stats', '--hierarchical']) + +test.execute() + +test.file_grep(test.obj_dir + "/Vsub/sub.sv", r'^module\s+(\S+)\s+', "sub") +test.file_grep(test.stats, r'HierBlock,\s+Hierarchical blocks\s+(\d+)', 1) + +test.passes() diff --git a/test_regress/t/t_hier_block_int.v b/test_regress/t/t_hier_block_int.v new file mode 100644 index 000000000..8ff804d88 --- /dev/null +++ b/test_regress/t/t_hier_block_int.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2025 by Antmicro. 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 + +module t(/*AUTOARG*/ + // inputs + clk +); + input clk; + + byte out1; + shortint out2; + int out3; + longint out4; + integer out5; + time out6; + + sub sub(out1, out2, out3, out4, out5, out6); + + always_ff @(posedge clk) begin + if (out1 == 1 && out2 == 2 && out3 == 3 && out4 == 4 && out5 == 5 && out6 == 6) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Mismatch\n"); + $stop; + end + end +endmodule + +module sub( + output byte out1, + output shortint out2, + output int out3, + output longint out4, + output integer out5, + output time out6 +); /*verilator hier_block*/ + assign out1 = 1; + assign out2 = 2; + assign out3 = 3; + assign out4 = 4; + assign out5 = 5; + assign out6 = 6; +endmodule From bed9aa9904e7bcaace68674c0f838fc162bb7e13 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 21 Jan 2025 19:51:43 -0500 Subject: [PATCH 44/93] Internals: Cleanup std::string empty string constructors. --- src/V3EmitCFunc.cpp | 15 +++++++++------ src/V3EmitCFunc.h | 1 + 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/src/V3EmitCFunc.cpp b/src/V3EmitCFunc.cpp index d39c8c4b0..97335c62e 100644 --- a/src/V3EmitCFunc.cpp +++ b/src/V3EmitCFunc.cpp @@ -456,9 +456,7 @@ void EmitCFunc::emitDereference(AstNode* nodep, const string& pointer) { void EmitCFunc::emitCvtPackStr(AstNode* nodep) { if (const AstConst* const constp = VN_CAST(nodep, Const)) { - putnbs(nodep, "std::string{"); - putsQuoted(constp->num().toString()); - puts("}"); + emitConstantString(constp); } else if (VN_IS(nodep->dtypep(), StreamDType)) { putnbs(nodep, "VL_CVT_PACK_STR_ND("); iterateAndNextConstNull(nodep); @@ -494,9 +492,7 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string } else if (nodep->num().isFourState()) { nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context"); } else if (nodep->num().isString()) { - putnbs(nodep, "std::string{"); - putsQuoted(nodep->num().toString()); - puts("}"); + emitConstantString(nodep); } else if (nodep->isWide()) { int upWidth = nodep->num().widthMin(); int chunks = 0; @@ -594,6 +590,13 @@ void EmitCFunc::emitConstant(AstConst* nodep, AstVarRef* assigntop, const string } } +void EmitCFunc::emitConstantString(const AstConst* nodep) { + putnbs(nodep, "std::string{"); + const string str = nodep->num().toString(); + if (!str.empty()) putsQuoted(str); + puts("}"); +} + void EmitCFunc::emitSetVarConstant(const string& assignString, AstConst* constp) { if (!constp->isWide()) { puts(assignString); diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index e79109502..3512a7c58 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -208,6 +208,7 @@ public: void emitCvtPackStr(AstNode* nodep); void emitCvtWideArray(AstNode* nodep, AstNode* fromp); void emitConstant(AstConst* nodep, AstVarRef* assigntop, const string& assignString); + void emitConstantString(const AstConst* nodep); void emitSetVarConstant(const string& assignString, AstConst* constp); void emitVarReset(AstVar* varp); string emitVarResetRecurse(const AstVar* varp, const string& varNameProtected, From d8080008b46610450b2223aca829425d26538608 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 21 Jan 2025 22:00:04 -0500 Subject: [PATCH 45/93] Commentary --- src/V3AstNodeExpr.h | 2 +- src/V3Const.cpp | 2 +- src/V3EmitCFunc.h | 18 ++++++++---------- test_regress/t/t_sc_names.cpp | 2 +- 4 files changed, 11 insertions(+), 13 deletions(-) diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index db32a6de9..f3e418a37 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -4151,7 +4151,7 @@ public: return new AstArraySel{fileline(), lhsp, rhsp}; } void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override { - V3ERROR_NA; /* How can from be a const? */ + V3ERROR_NA; // How can from be a const? } string emitVerilog() override { return "%k(%l%f[%r])"; } string emitC() override { return "%li%k[%ri]"; } diff --git a/src/V3Const.cpp b/src/V3Const.cpp index 406f4c815..d39760288 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -3377,7 +3377,7 @@ class ConstVisitor final : public VNVisitor { void visit(AstJumpGo* nodep) override { iterateChildren(nodep); - // Jump to label where label immediately follows this go is not useful + // Jump to label where label immediately follows this JumpGo is not useful if (nodep->labelp() == VN_CAST(nodep->nextp(), JumpLabel)) { VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); // Keep the label, might be other jumps pointing to it, gets cleaned later diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 3512a7c58..2ffee3e8b 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -345,16 +345,14 @@ public: if (m_useSelfForThis) { m_usevlSelfRef = true; - /* - * Using reference to the vlSelf pointer will help the C++ - * compiler to have dereferenceable hints, which can help to - * reduce the need for branch instructions in the generated - * code to allow the compiler to generate load store after the - * if condition (including short-circuit evaluation) - * speculatively and also reduce the data cache pollution when - * executing in the wrong path to make verilator-generated code - * run faster. - */ + // Using reference to the vlSelf pointer will help the C++ + // compiler to have dereferenceable hints, which can help to + // reduce the need for branch instructions in the generated + // code to allow the compiler to generate load store after the + // if condition (including short-circuit evaluation) + // speculatively and also reduce the data cache pollution when + // executing in the wrong path to make verilator-generated code + // run faster. puts("auto& vlSelfRef = std::ref(*vlSelf).get();\n"); } diff --git a/test_regress/t/t_sc_names.cpp b/test_regress/t/t_sc_names.cpp index 354d27ce2..3d7b6cd7f 100644 --- a/test_regress/t/t_sc_names.cpp +++ b/test_regress/t/t_sc_names.cpp @@ -14,7 +14,7 @@ int sc_main(int argc, char* argv[]) { std::vector ch = tb->get_child_objects(); bool found = false; - /* We expect to find clk in here. */ + // We expect to find clk in here for (int i = 0; i < ch.size(); ++i) { if (!std::strcmp(ch[i]->basename(), "clk")) found = true; } From d4f4fefd1d5b8d7585c9d218e3e2650099d45b99 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 24 Jan 2025 17:30:59 -0500 Subject: [PATCH 46/93] Fix `-j` ignored after `-f` (#5749). --- Changes | 1 + src/V3Options.cpp | 11 ++++++----- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/Changes b/Changes index c0188f094..270e7fdf8 100644 --- a/Changes +++ b/Changes @@ -36,6 +36,7 @@ Verilator 5.033 devel * Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] * Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732). [Andrew Nolte] * Fix duplicate-named class variable equivalence (#5737). +* Fix `-j` ignored after `-f` (#5749). [Luca Colagrande] * Fix matching language extension options including dots. diff --git a/src/V3Options.cpp b/src/V3Options.cpp index f4dc0f573..ce99fbebd 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -955,9 +955,6 @@ void V3Options::notify() VL_MT_DISABLED { cmdfl->v3error("Unsupported: --timing and --savable not supported together"); } - // Mark options as available - m_available = true; - // --dump-tree-dot will turn on tree dumping. if (!m_dumpLevel.count("tree") && m_dumpLevel.count("tree-dot")) { m_dumpLevel["tree"] = m_dumpLevel["tree-dot"]; @@ -965,9 +962,15 @@ void V3Options::notify() VL_MT_DISABLED { // Sanity check of expected configuration UASSERT(threads() >= 1, "'threads()' must return a value >= 1"); + if (m_buildJobs == -1) m_buildJobs = 1; + if (m_verilateJobs == -1) m_verilateJobs = 1; // Preprocessor defines based on options used if (timing().isSetTrue()) V3PreShell::defineCmdLine("VERILATOR_TIMING", "1"); + + // === Leave last + // Mark options as available + m_available = true; } //###################################################################### @@ -1853,8 +1856,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, ++i; } } - if (m_buildJobs == -1) m_buildJobs = 1; - if (m_verilateJobs == -1) m_verilateJobs = 1; } //====================================================================== From 432d5f851df2e562c6b96a652c9b99bf758275dd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 24 Jan 2025 20:52:31 -0500 Subject: [PATCH 47/93] Internals: Cleanup some misnamed nodep's. No functional change. --- src/V3LinkDot.cpp | 10 +++++----- src/V3Param.cpp | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 8c54b2984..9f77c8aad 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -986,8 +986,8 @@ class LinkDotFindVisitor final : public VNVisitor { if (nodep->hierParams()) { UINFO(1, "Found module with hier type parameters" << endl); m_hierParamsName = nodep->name(); - for (const AstNode* node = nodep->op2p(); node; node = node->nextp()) { - if (const AstTypedef* const tdef = VN_CAST(node, Typedef)) { + for (const AstNode* stmtp = nodep->stmtsp(); stmtp; stmtp = stmtp->nextp()) { + if (const AstTypedef* const tdef = VN_CAST(stmtp, Typedef)) { UINFO(1, "Inserting hier type parameter typedef: " << tdef << endl); VSymEnt* const upperSymp = m_curSymp ? m_curSymp : m_statep->rootEntp(); m_curSymp = m_modSymp = m_statep->insertBlock(upperSymp, nodep->name(), @@ -1501,9 +1501,9 @@ class LinkDotFindVisitor final : public VNVisitor { if (const VSymEnt* const typedefEntp = m_curSymp->findIdFallback(m_hierParamsName)) { const AstModule* modp = VN_CAST(typedefEntp->nodep(), Module); - for (const AstNode* node = modp ? modp->stmtsp() : nullptr; node; - node = node->nextp()) { - const AstTypedef* tdefp = VN_CAST(node, Typedef); + for (const AstNode* stmtp = modp ? modp->stmtsp() : nullptr; stmtp; + stmtp = stmtp->nextp()) { + const AstTypedef* tdefp = VN_CAST(stmtp, Typedef); if (tdefp && tdefp->name() == nodep->name() && m_statep->forPrimary()) { UINFO(8, "Replacing type of" << nodep << endl diff --git a/src/V3Param.cpp b/src/V3Param.cpp index 17ecb785b..5cea41c8b 100644 --- a/src/V3Param.cpp +++ b/src/V3Param.cpp @@ -543,18 +543,18 @@ class ParamProcessor final { for (auto&& defaultValue : paramsIt->second) { const auto pinIt = pins.find(defaultValue.first); // If the pin does not have a value assigned, use the default one. - const AstNode* const node = pinIt == pins.end() ? defaultValue.second : pinIt->second; + const AstNode* const nodep = pinIt == pins.end() ? defaultValue.second : pinIt->second; // This longname is not valid as verilog symbol, but ok, because it will be hashed longname += "_" + defaultValue.first + "="; // constp can be nullptr - if (const AstConst* const p = VN_CAST(node, Const)) { + if (const AstConst* const p = VN_CAST(nodep, Const)) { // Treat modules parametrized with the same values but with different type as the // same. longname += p->num().ascii(false); - } else if (node) { + } else if (nodep) { std::stringstream type; - V3EmitV::verilogForTree(node, type); + V3EmitV::verilogForTree(nodep, type); longname += type.str(); } } From 929e15fa4c61751a247ae6b3cc17bcf32bddb86b Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 24 Jan 2025 21:00:45 -0500 Subject: [PATCH 48/93] Fix various round-trip Verilog output, including packed arrays --- src/V3EmitV.cpp | 190 +++++++++++++++++++++---------- test_regress/t/t_debug_emitv.out | 166 +++++++++++---------------- test_regress/t/t_debug_emitv.py | 13 ++- test_regress/t/t_debug_emitv.v | 36 ++++-- 4 files changed, 233 insertions(+), 172 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index c61d3c0cc..8aab51688 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -28,13 +28,17 @@ VL_DEFINE_DEBUG_FUNCTIONS; // Emit statements and expressions class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { - // MEMBERS - bool m_suppressSemi = false; + // STATE - across all visitors const bool m_suppressUnknown = false; + + // STATE - for current visit position (use VL_RESTORER) AstSenTree* m_sensesp; // Domain for printing one a ALWAYS under a ACTIVE + bool m_suppressSemi = false; // Non-statement, don't print ; + bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars + bool m_arrayPost = false; // Print array information that goes after identifier (vs after) + std::deque m_packedps; // Packed arrays to print with BasicDType // METHODS - virtual void puts(const string& str) = 0; virtual void putbs(const string& str) = 0; virtual void putfs(AstNode* nodep, const string& str) = 0; // Fileline and node %% mark @@ -49,6 +53,20 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putsNoTracking("\""); } + void iterateAndCommaConstNull(AstNode* nodep) { + for (; nodep; nodep = nodep->nextp()) { + iterateConst(nodep); + if (nodep->nextp()) puts(", "); + } + } + void emitPacked() { + for (AstNodeArrayDType* packedp : m_packedps) { + puts(" "); + iterateConstNull(packedp->rangep()); + } + m_packedps.clear(); + } + // VISITORS void visit(AstNetlist* nodep) override { iterateAndNextConstNull(nodep->modulesp()); } void visit(AstNodeModule* nodep) override { @@ -58,13 +76,14 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstPort* nodep) override {} void visit(AstNodeFTask* nodep) override { - putfs(nodep, nodep->isFunction() ? "function" : "task"); + const bool func = nodep->isFunction() || nodep->name() == "new"; + putfs(nodep, func ? "function" : "task"); puts(" "); puts(nodep->prettyName()); puts(";\n"); // Only putfs the first time for each visitor; later for same node is putqs iterateAndNextConstNull(nodep->stmtsp()); - putfs(nodep, nodep->isFunction() ? "endfunction\n" : "endtask\n"); + putfs(nodep, func ? "endfunction\n" : "endtask\n"); } void visit(AstBegin* nodep) override { @@ -169,7 +188,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstSenItem* nodep) override { putfs(nodep, ""); - puts(nodep->edgeType().verilogKwd()); + if (nodep->edgeType() != VEdgeType::ET_CHANGED) puts(nodep->edgeType().verilogKwd()); if (nodep->sensp()) puts(" "); iterateChildrenConst(nodep); } @@ -221,13 +240,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, nodep->verilogKwd()); putbs("("); if (fileOrStrgp) { - iterateAndNextConstNull(fileOrStrgp); + iterateConstNull(fileOrStrgp); putbs(", "); } putsQuoted(text); for (AstNode* expp = exprsp; expp; expp = expp->nextp()) { puts(", "); - iterateAndNextConstNull(expp); + iterateConstNull(expp); } puts(");\n"); } @@ -393,7 +412,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstTextBlock* nodep) override { visit(static_cast(nodep)); - VL_RESTORER(m_suppressSemi); + VL_RESTORER(m_suppressVarSemi); m_suppressVarSemi = nodep->commas(); for (AstNode* childp = nodep->nodesp(); childp; childp = childp->nextp()) { iterateConst(childp); @@ -403,17 +422,17 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstScopeName* nodep) override {} void visit(AstCStmt* nodep) override { putfs(nodep, "$_CSTMT("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(");\n"); } void visit(AstCExpr* nodep) override { putfs(nodep, "$_CEXPR("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(")"); } void visit(AstUCStmt* nodep) override { putfs(nodep, "$c("); - iterateAndNextConstNull(nodep->exprsp()); + iterateAndCommaConstNull(nodep->exprsp()); puts(");\n"); } void visit(AstUCFunc* nodep) override { @@ -431,19 +450,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstCMethodHard* nodep) override { iterateConst(nodep->fromp()); puts("." + nodep->name() + "("); - for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) { - if (pinp != nodep->pinsp()) puts(", "); - iterateConst(pinp); - } + iterateAndCommaConstNull(nodep->pinsp()); puts(")"); } void visit(AstCMethodCall* nodep) override { iterateConst(nodep->fromp()); puts("." + nodep->name() + "("); - for (AstNode* pinp = nodep->argsp(); pinp; pinp = pinp->nextp()) { - if (pinp != nodep->argsp()) puts(", "); - iterateConst(pinp); - } + iterateAndCommaConstNull(nodep->argsp()); puts(")"); } @@ -565,13 +578,15 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts(cvtToStr(nodep->leftConst())); puts(":"); puts(cvtToStr(nodep->rightConst())); - puts("]"); } else { iterateAndNextConstNull(nodep->leftp()); puts(":"); iterateAndNextConstNull(nodep->rightp()); - puts("]"); } + puts("]"); + } + void visit(AstRand* nodep) override { + emitVerilogFormat(nodep, nodep->emitVerilog(), nodep->seedp()); } void visit(AstSel* nodep) override { iterateAndNextConstNull(nodep->fromp()); @@ -603,17 +618,31 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstTypedef* nodep) override { putfs(nodep, "typedef "); - iterateAndNextConstNull(nodep->subDTypep()); + iterateConstNull(nodep->subDTypep()); puts(" "); puts(nodep->prettyName()); puts(";\n"); } + void visit(AstAssocArrayDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + puts("["); + iterateConst(nodep->keyDTypep()); + puts("]"); + m_arrayPost = true; + iterateConst(nodep->subDTypep()); // For post's key + } + } void visit(AstBasicDType* nodep) override { + if (m_arrayPost) return; putfs(nodep, nodep->prettyName()); - if (nodep->isSigned()) putfs(nodep, " signed"); + if (nodep->isSigned() && !nodep->keyword().isDouble()) putfs(nodep, " signed"); // Do not emit ranges for integer atoms. if (nodep->keyword().isIntNumeric() && !nodep->keyword().isBitLogic()) return; - + emitPacked(); if (nodep->rangep()) { puts(" "); iterateAndNextConstNull(nodep->rangep()); @@ -627,12 +656,50 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } } void visit(AstConstDType* nodep) override { + if (m_arrayPost) return; putfs(nodep, "const "); iterateConst(nodep->subDTypep()); } - void visit(AstNodeArrayDType* nodep) override { + void visit(AstDynArrayDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + puts("[]"); + iterateConst(nodep->subDTypep()); // For post's key + } + } + void visit(AstEnumDType* nodep) override { + if (m_arrayPost) return; + putfs(nodep, "enum "); iterateConst(nodep->subDTypep()); - iterateAndNextConstNull(nodep->rangep()); + puts("{\n"); + iterateAndNextConstNull(nodep->itemsp()); + puts("}"); + } + void visit(AstEnumItem* nodep) override { + putfs(nodep, nodep->name()); + iterateConstNull(nodep->rangep()); + puts(" = "); + iterateConstNull(nodep->valuep()); + if (nodep->nextp()) puts(","); + puts("\n"); + } + void visit(AstNodeArrayDType* nodep) override { + if (!m_arrayPost) { + if (VN_IS(nodep, PackArrayDType)) { + // Unpacked ranges handled in BasicDType, as they print "backwards" + m_packedps.push_back(nodep); + } + iterateConst(nodep->subDTypep()); + } else { + if (VN_IS(nodep, UnpackArrayDType)) { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + iterateAndNextConstNull(nodep->rangep()); + m_arrayPost = true; + } + iterateConst(nodep->subDTypep()); // For post's key + } } void visit(AstRefDType* nodep) override { if (nodep->subDTypep()) { @@ -642,21 +709,43 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } } void visit(AstNodeUOrStructDType* nodep) override { + if (m_arrayPost) return; puts(nodep->verilogKwd() + " "); if (nodep->packed()) puts("packed "); - puts("\n"); - puts("{"); - for (AstMemberDType* itemp = nodep->membersp(); itemp; - itemp = VN_AS(itemp->nextp(), MemberDType)) { - iterateConst(itemp); - puts(";"); + { + puts("{\n"); + VL_RESTORER(m_packedps); + m_packedps.clear(); + for (AstMemberDType* itemp = nodep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + iterateConst(itemp); + } + puts("}"); } - puts("}"); + emitPacked(); } void visit(AstMemberDType* nodep) override { + if (m_arrayPost) return; iterateConst(nodep->subDTypep()); puts(" "); puts(nodep->name()); + puts(";\n"); + } + void visit(AstQueueDType* nodep) override { + if (!m_arrayPost) { + iterateConst(nodep->subDTypep()); + } else { + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + puts("[$"); + if (nodep->boundp()) { + puts(":"); + iterateConst(nodep->boundp()); + } + puts("]"); + m_arrayPost = true; + iterateConst(nodep->subDTypep()); // For post's key + } } void visit(AstNodeFTaskRef* nodep) override { if (nodep->dotted() != "") { @@ -719,37 +808,23 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { putfs(nodep, nodep->verilogKwd()); puts(" "); } - std::vector unpackps; - for (AstNodeDType* dtypep = nodep->dtypep(); dtypep;) { - dtypep = dtypep->skipRefp(); - if (const AstUnpackArrayDType* const unpackp = VN_CAST(dtypep, UnpackArrayDType)) { - unpackps.push_back(unpackp); - dtypep = unpackp->subDTypep(); - } else { - iterateConst(dtypep); - puts(" "); - puts(nodep->prettyName()); - dtypep = nullptr; - } - } - // If nodep is an unpacked array, append unpacked dimensions - for (const auto& unpackp : unpackps) { - puts("["); - puts(cvtToStr(unpackp->rangep()->leftConst())); - puts(":"); - puts(cvtToStr(unpackp->rangep()->rightConst())); - puts("]"); - } + VL_RESTORER(m_arrayPost); + m_arrayPost = false; + iterateConstNull(nodep->dtypep()); // Dtype part before identifier + puts(" "); + puts(nodep->prettyName()); + m_arrayPost = true; + iterateConstNull(nodep->dtypep()); // Dtype part after identifier puts(m_suppressVarSemi ? "\n" : ";\n"); } void visit(AstActive* nodep) override { + VL_RESTORER(m_sensesp); m_sensesp = nodep->sensesp(); iterateAndNextConstNull(nodep->stmtsp()); - m_sensesp = nullptr; } void visit(AstParseRef* nodep) override { puts(nodep->prettyName()); } - void visit(AstVarScope*) override {} void visit(AstNodeText*) override {} + void visit(AstVarScope*) override {} void visit(AstTraceDecl*) override {} void visit(AstTraceInc*) override {} // NOPs @@ -767,7 +842,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } public: - bool m_suppressVarSemi = false; // Suppress emitting semicolon for AstVars explicit EmitVBaseVisitorConst(bool suppressUnknown, AstSenTree* domainp) : m_suppressUnknown{suppressUnknown} , m_sensesp{domainp} {} diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 4e2b89ac7..d7337c931 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -1,94 +1,65 @@ module Vt_debug_emitv_t; input logic clk; input logic in; - typedef - ???? // ENUMDTYPE 't.e_t' - - ???? // ENUMITEM 'ZERO' - 32'h0 - ???? // ENUMITEM 'ONE' - 32'h1int signedstruct packed - {int signed a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed a;}bit [31:0] const struct packed - {int signed a;}const struct packed - {int signed a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed - ???? // QUEUEDTYPE - int signedstring - ???? // ASSOCARRAYDTYPE - int signed - ???? // DYNARRAYDTYPE - int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed e_t; - typedef struct packed - {int signed a;}logic signed [2:0] struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed a;}bit [31:0] const struct packed - {int signed a;}const struct packed - {int signed a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed - ???? // QUEUEDTYPE - int signedstring - ???? // ASSOCARRAYDTYPE - int signed - ???? // DYNARRAYDTYPE - int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed ps_t; - typedef struct - {logic signed [2:0] a;}logicunion - {logic a;}struct packed - {int signed a;}bit [31:0] const struct packed - {int signed a;}const struct packed - {int signed a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed - ???? // QUEUEDTYPE - int signedstring - ???? // ASSOCARRAYDTYPE - int signed - ???? // DYNARRAYDTYPE - int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed us_t; - typedef union - {logic a;}struct packed - {int signed a;}bit [31:0] const struct packed - {int signed a;}const struct packed - {int signed a;}[0:2]struct - {logic signed [2:0] a;}union - {logic a;}int signedint signed[0:2]logic [15:0] logic [15:0] logic [15:0] int signedint signedint signed - ???? // QUEUEDTYPE - int signedstring - ???? // ASSOCARRAYDTYPE - int signed - ???? // DYNARRAYDTYPE - int signedint signedint signedint signedint signedint signedint signedreal signedstringIDatalogic signed [31:0] int signed union_t; - struct packed - {int signed a;} ps[0:2]; - struct - {logic signed [2:0] a;} us; - union - {logic a;} unu; + typedef enum logic [2:0] { + ZERO = 3'h0, + ONE = 3'h1 + } e_t; + typedef struct packed { + logic [2:0] a; + } ps_t; + typedef struct { + logic signed [2:0] a; + } us_t; + typedef union { + logic a; + } union_t; + const struct packed { + logic [2:0] a; + } ps[0:2]; + struct { + logic signed [2:0] a; + } us; + union { + logic a; + } unu; int signed array[0:2]; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end + bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; logic [15:0] pubflat; logic [15:0] pubflat_r; logic [15:0] pubflat_w; assign pubflat_w = pubflat; int signed fd; int signed i; - - ???? // QUEUEDTYPE - q; - - ???? // ASSOCARRAYDTYPE - assoc; - - ???? // DYNARRAYDTYPE - dyn; + int signed q[$]; + int signed qb[$:'sh3]; + int signed assoc[string]; + int signed assocassoc[string][real]; + int signed dyn[]; + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } nibble_t; + struct packed { + struct packed { + logic nn2; + } nested_anonymous; + struct packed { + logic nn1; + } nested_named; + logic [11:10] nn3; + } [5:4] nibblearray[3:2]; task t; $display("stmt"); endtask @@ -111,7 +82,7 @@ module Vt_debug_emitv_t; begin other = f(i); $display("stmt %~ %~", - iother, other); + i, other); t(); end i = (i + 'h1); @@ -128,7 +99,7 @@ module Vt_debug_emitv_t; $display("stmt"); end end - always @([changed] in) begin + always @( in) begin begin $display("stmt"); end @@ -147,7 +118,7 @@ module Vt_debug_emitv_t; int signed cyc; int signed fo; int signed sum; - real signed r; + real r; string str; always @(posedge clk) begin begin @@ -156,7 +127,7 @@ module Vt_debug_emitv_t; fo = cyc; sub.inc(fosum); sum = sub.f(sum); - $display("[%0t] sum = %~", $timesum, sum); + $display("[%0t] sum = %~", $time, sum); $display("a?= %d", ($c('sh1) ? $c('sh14) : $c('sh1e))); $c(;); @@ -248,32 +219,23 @@ module Vt_debug_emitv_t; else begin $display("0"); end - $display("%~%~", $past(cyc)$past(cyc, 'sh1), - $past(cyc, 'sh1)); + $display("%~%~", $past(cyc), $past(cyc, + 'sh1)); str = $sformatf("cyc=%~", cyc); ; $display("str = %@", str); - $display("%% [%t] [%^] to=%o td=%d", $time - $realtime$time$time, $realtime - $time$time, $time$time, $time); + $display("%% [%t] [%^] to=%o td=%d", $time, + $realtime, $time, $time); $sscanf(40'h666f6f3d35, "foo=%d", i); ; $printtimescale; if ((i != 'sh5)) begin $stop; end - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; - sum = - ???? // RAND - ; - sum = - ???? // RAND - 'sha; + sum = $random(); + sum = $random('sha); + sum = $urandom(); + sum = $urandom('sha); if ((PKG_PARAM != 'sh1)) begin $stop; end @@ -318,8 +280,8 @@ package Vt_debug_emitv___024unit; member = 'sh1; task method; endtask - task new; - endtask + function new; + endfunction endclass endpackage module Vt_debug_emitv_sub; @@ -339,7 +301,7 @@ module Vt_debug_emitv_sub; disable label0; end endfunction - real signed r; + real r; endmodule package Vt_debug_emitv_p; logic pkgvar; diff --git a/test_regress/t/t_debug_emitv.py b/test_regress/t/t_debug_emitv.py index 6063ac2c8..227968047 100755 --- a/test_regress/t/t_debug_emitv.py +++ b/test_regress/t/t_debug_emitv.py @@ -16,7 +16,16 @@ test.lint( # Likewise XML v_flags=["--lint-only --dumpi-tree 9 --dumpi-V3EmitV 9 --debug-emitv"]) -test.files_identical(test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v"), - test.golden_filename) +output_v = test.glob_one(test.obj_dir + "/" + test.vm_prefix + "_*_width.tree.v") + +test.files_identical(output_v, test.golden_filename) + +if test.verbose: + # Print if that the output Verilog is clean + # TODO not yet round-trip clean + test.run(cmd=[os.environ["VERILATOR_ROOT"] + "/bin/verilator", "--lint-only", output_v], + logfile=test.obj_dir + "/sim_roundtrip.log", + fails=True, + verilator_run=True) test.passes() diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 989888d8c..8c7ba061a 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -33,7 +33,7 @@ module t (/*AUTOARG*/ // verilator lint_off UNPACKED - typedef enum { + typedef enum [2:0] { ZERO, ONE = 1 } e_t; @@ -52,19 +52,35 @@ module t (/*AUTOARG*/ us_t us; union_t unu; - int array[3]; + int array[3]; initial array = '{1,2,3}; - reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; + bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; - reg [15:0] pubflat_r; - wire [15:0] pubflat_w = pubflat; - int fd; - int i; + reg [15:0] pubflat /*verilator public_flat_rw @(posedge clk) */; - int q[$]; - int assoc[string]; - int dyn[]; + reg [15:0] pubflat_r; + wire [15:0] pubflat_w = pubflat; + int fd; + int i; + + int q[$]; + int qb[$ : 3]; + int assoc[string]; + int assocassoc[string][real]; + int dyn[]; + + typedef struct packed { + logic nn1; + } nested_named_t; + typedef struct packed { + struct packed { + logic nn2; + } nested_anonymous; + nested_named_t nested_named; + logic [11:10] nn3; + } nibble_t; + nibble_t [5:4] nibblearray[3:2]; task t; $display("stmt"); From 7e6dd9473f09d40b8276512f76ca0adcda34b5f8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 24 Jan 2025 21:27:52 -0500 Subject: [PATCH 49/93] Commentary: Changes update --- Changes | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Changes b/Changes index 270e7fdf8..b16657146 100644 --- a/Changes +++ b/Changes @@ -20,7 +20,9 @@ Verilator 5.033 devel * Support generated classes (#5665). [Shou-Li Hsu] * Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang] * Support `+incdir` with multiple directories. +* Support integer atom type ports in `--hierarchical` (#5748). [Bartłomiej Chmiel, Antmicro Ltd.] * Support side effects of form 'variable[index_function()]++'. +* Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.] * Improve VPI write errors (#5712). [Andrew Nolte] * Improve `resetall support (#5728) (#5730). [Ethan Sifferman] * Fix error message when call task as a function (#3089). [Matthew Ballance] @@ -35,6 +37,7 @@ Verilator 5.033 devel * Fix segfault when only enum value referenced in package (#5714). [Dan Katz] * Fix `BLKSEQ` on suspendable processes (#5722). [Krzysztof Bieganski, Antmicro Ltd.] * Fix vpiDefName issues with non-inlined scopes and dpi conflicts (#5732). [Andrew Nolte] +* Fix inlined expression with assignment under LHS of NBA (#5736) (#5740). [Geza Lore] * Fix duplicate-named class variable equivalence (#5737). * Fix `-j` ignored after `-f` (#5749). [Luca Colagrande] * Fix matching language extension options including dots. From 6281385ee8162437d44b172a5bd1547e6478bff9 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 24 Jan 2025 21:31:57 -0500 Subject: [PATCH 50/93] Commentary/Internals: Sort option names. No functional change. --- bin/verilator | 12 +++---- docs/guide/exe_verilator.rst | 64 ++++++++++++++++----------------- src/V3Options.cpp | 68 +++++++++++++++++++----------------- src/V3Options.h | 12 +++---- 4 files changed, 80 insertions(+), 76 deletions(-) diff --git a/bin/verilator b/bin/verilator index 756344a25..cf6512913 100755 --- a/bin/verilator +++ b/bin/verilator @@ -374,8 +374,8 @@ detailed descriptions of these arguments. --gdb Run Verilator under GDB interactively --gdbbt Run Verilator under GDB for backtrace --generate-key Create random key for --protect-key - --getenv Get environment variable with defaults --get-supported Get if feature is supported + --getenv Get environment variable with defaults --help Show this help --hierarchical Enable hierarchical Verilation --hierarchical-params-file Internal option that specifies parameters file for hier blocks @@ -385,6 +385,11 @@ detailed descriptions of these arguments. --inline-mult Tune module inlining --instr-count-dpi Assumed dynamic instruction count of DPI imports -j Parallelism for --build-jobs/--verilate-jobs + --no-json-edit-nums Don't dump editNum in .tree.json files + --no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json + --json-only Create JSON parser output (.tree.json and .meta.json) + --json-only-output .tree.json output filename + --json-only-meta-output .tree.meta.json output filename --l2-name Verilog scope name of the top module --language Default language standard to parse -LDFLAGS Linker pre-object arguments for makefile @@ -506,11 +511,6 @@ detailed descriptions of these arguments. --x-assign Assign non-initial Xs to this value --x-initial Assign initial Xs to this value --x-initial-edge Enable initial X->0 and X->1 edge triggers - --no-json-edit-nums Don't dump editNum in .tree.json files - --no-json-ids Don't use short identifiers instead of adresses/paths in .tree.json - --json-only Create JSON parser output (.tree.json and .meta.json) - --json-only-output .tree.json output filename - --json-only-meta-output .tree.meta.json output filename --xml-only Create XML parser output --xml-output XML output filename -y Directory to search for modules diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 03f8a2454..c73301b22 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -782,6 +782,38 @@ Summary: the number of threads in the current hardware. Otherwise, must be a positive integer specifying the maximum number of parallel build jobs. +.. option:: --json-only + + Create JSON output only, do not create any other output. + + The JSON format is intended to be used to leverage Verilator's parser and + elaboration to feed to other downstream tools. For details on the format, see + the Verilator Internals manual. Be aware that the JSON + format is still evolving; there will be some changes in future versions. + + This option disables some more aggressive transformations and dumps only + the final state of the AST. For more granular and unaltered dumps, meant + mainly for debugging see :vlopt:`--dump-tree-json`. + +.. option:: --json-only-meta-output + + Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only. + Using this option automatically sets :vlopt:`--json-only`. + +.. option:: --json-only-output + + Specifies the filename for the main output file (`.tree.json`) of --json-only. + Using this option automatically sets :vlopt:`--json-only`. + +.. option:: --no-json-edit-nums + + Don't dump edit number in .tree.json files. This may make the file more + run-to-run stable for easier comparison. + +.. option:: --no-json-ids + + Don't use short identifiers instead of addresses/paths in .tree.json. + .. option:: --l2-name Instead of using the module name when showing Verilog scope, use the @@ -1893,38 +1925,6 @@ Summary: iterations. This may be another indication of problems with the modeled design that should be addressed. -.. option:: --json-only - - Create JSON output only, do not create any other output. - - The JSON format is intended to be used to leverage Verilator's parser and - elaboration to feed to other downstream tools. For details on the format, see - the Verilator Internals manual. Be aware that the JSON - format is still evolving; there will be some changes in future versions. - - This option disables some more aggressive transformations and dumps only - the final state of the AST. For more granular and unaltered dumps, meant - mainly for debugging see :vlopt:`--dump-tree-json`. - -.. option:: --json-only-meta-output - - Specifies the filename for the metadata output file (`.tree.meta.json`) of --json-only. - Using this option automatically sets :vlopt:`--json-only`. - -.. option:: --json-only-output - - Specifies the filename for the main output file (`.tree.json`) of --json-only. - Using this option automatically sets :vlopt:`--json-only`. - -.. option:: --no-json-edit-nums - - Don't dump edit number in .tree.json files. This may make the file more - run-to-run stable for easier comparison. - -.. option:: --no-json-ids - - Don't use short identifiers instead of addresses/paths in .tree.json. - .. option:: --xml-only Create XML output only, do not create any other output. diff --git a/src/V3Options.cpp b/src/V3Options.cpp index ce99fbebd..bc9a7c9d5 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1236,8 +1236,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } }); DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes)); - DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); DECL_OPTION("-converge-limit", Set, &m_convergeLimit); + DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); DECL_OPTION("-coverage-line", OnOff, &m_coverageLine); DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth); DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle); @@ -1280,8 +1280,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-dumpi-", CbPartialMatchVal, [this](const char* optp, const char* valp) { m_dumpLevel[optp] = std::atoi(valp); }); - DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums); - DECL_OPTION("-json-ids", OnOff, &m_jsonIds); + DECL_OPTION("-E", CbOnOff, [this](bool flag) { if (flag) { m_stdPackage = false; @@ -1314,6 +1313,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-fconst", FOnOff, &m_fConst); DECL_OPTION("-fconst-before-dfg", FOnOff, &m_fConstBeforeDfg); DECL_OPTION("-fconst-bit-op-tree", FOnOff, &m_fConstBitOpTree); + DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns); + DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells); DECL_OPTION("-fdedup", FOnOff, &m_fDedupe); DECL_OPTION("-fdfg", CbFOnOff, [this](bool flag) { m_fDfgPreInline = flag; @@ -1328,8 +1329,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-fdfg-pre-inline", FOnOff, &m_fDfgPreInline); DECL_OPTION("-fdfg-post-inline", FOnOff, &m_fDfgPostInline); - DECL_OPTION("-fdead-assigns", FOnOff, &m_fDeadAssigns); - DECL_OPTION("-fdead-cells", FOnOff, &m_fDeadCells); DECL_OPTION("-fexpand", FOnOff, &m_fExpand); DECL_OPTION("-ffunc-opt", CbFOnOff, [this](bool flag) { // m_fFuncSplitCat = flag; @@ -1391,6 +1390,18 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, if (m_instrCountDpi < 0) fl->v3fatal("--instr-count-dpi must be non-negative: " << val); }); + DECL_OPTION("-json-edit-nums", OnOff, &m_jsonEditNums); + DECL_OPTION("-json-ids", OnOff, &m_jsonIds); + DECL_OPTION("-json-only", OnOff, &m_jsonOnly); + DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) { + m_jsonOnlyMetaOutput = valp; + m_jsonOnly = true; + }); + DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) { + m_jsonOnlyOutput = valp; + m_jsonOnly = true; + }); + DECL_OPTION("-LDFLAGS", CbVal, callStrSetter(&V3Options::addLdLibs)); DECL_OPTION("-l2-name", Set, &m_l2Name); DECL_OPTION("-no-l2name", CbCall, [this]() { m_l2Name = ""; }).undocumented(); // Historical @@ -1415,7 +1426,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-lint-only", OnOff, &m_lintOnly); DECL_OPTION("-localize-max-size", Set, &m_localizeMaxSize); - DECL_OPTION("-main-top-name", Set, &m_mainTopName); DECL_OPTION("-MAKEFLAGS", CbVal, callStrSetter(&V3Options::addMakeFlags)); DECL_OPTION("-MMD", OnOff, &m_makeDepend); @@ -1425,6 +1435,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, addIncDirFallback(m_makeDir); // Need to find generated files there too }); DECL_OPTION("-main", OnOff, &m_main); + DECL_OPTION("-main-top-name", Set, &m_mainTopName); DECL_OPTION("-make", CbVal, [this, fl](const char* valp) { if (!std::strcmp(valp, "cmake")) { m_cmake = true; @@ -1449,6 +1460,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-order-clock-delay", CbOnOff, [fl](bool /*flag*/) { fl->v3warn(DEPRECATED, "Option order-clock-delay is deprecated and has no effect."); }); + DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) { + m_outputGroups = std::atoi(valp); + if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); } + }); DECL_OPTION("-output-split", Set, &m_outputSplit); DECL_OPTION("-output-split-cfuncs", CbVal, [this, fl](const char* valp) { m_outputSplitCFuncs = std::atoi(valp); @@ -1462,20 +1477,15 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, fl->v3error("--output-split-ctrace must be >= 0: " << valp); } }); - DECL_OPTION("-output-groups", CbVal, [this, fl](const char* valp) { - m_outputGroups = std::atoi(valp); - if (m_outputGroups < 0) { fl->v3error("--output-groups must be >= 0: " << valp); } - }); DECL_OPTION("-P", Set, &m_preprocNoLine); - DECL_OPTION("-pvalue+", CbPartialMatch, - [this](const char* varp) { addParameter(varp, false); }); DECL_OPTION("-pins64", CbCall, [this]() { m_pinsBv = 65; }); DECL_OPTION("-no-pins64", CbCall, [this]() { m_pinsBv = 33; }); DECL_OPTION("-pins-bv", CbVal, [this, fl](const char* valp) { m_pinsBv = std::atoi(valp); if (m_pinsBv > 65) fl->v3fatal("--pins-bv maximum is 65: " << valp); }); + DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables); DECL_OPTION("-pins-sc-uint", CbOnOff, [this](bool flag) { m_pinsScUint = flag; if (!m_pinsScBigUint) m_pinsBv = 65; @@ -1485,7 +1495,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_pinsScBigUint = flag; m_pinsBv = 513; }); - DECL_OPTION("-pins-inout-enables", OnOff, &m_pinsInoutEnables); DECL_OPTION("-pins-uint8", OnOff, &m_pinsUint8); DECL_OPTION("-pipe-filter", Set, &m_pipeFilter); DECL_OPTION("-pp-comments", OnOff, &m_ppComments); @@ -1496,10 +1505,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-private", CbCall, [this]() { m_public = false; }); DECL_OPTION("-prof-c", OnOff, &m_profC); DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; }); - DECL_OPTION("-profile-cfuncs", CbCall, - [this]() { m_profC = m_profCFuncs = true; }); // Renamed DECL_OPTION("-prof-exec", OnOff, &m_profExec); DECL_OPTION("-prof-pgo", OnOff, &m_profPgo); + DECL_OPTION("-profile-cfuncs", CbCall, + [this]() { m_profC = m_profCFuncs = true; }); // Renamed DECL_OPTION("-protect-ids", OnOff, &m_protectIds); DECL_OPTION("-protect-key", Set, &m_protectKey); DECL_OPTION("-protect-lib", CbVal, [this, fl](const char* valp) { @@ -1518,6 +1527,9 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_publicParams = flag; v3Global.dpi(true); }); + DECL_OPTION("-pvalue+", CbPartialMatch, + [this](const char* varp) { addParameter(varp, false); }); + DECL_OPTION("-quiet", CbOnOff, [this](bool flag) { m_quietExit = flag; m_quietStats = flag; @@ -1563,7 +1575,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-structs-packed", OnOff, &m_structsPacked); DECL_OPTION("-sv", CbCall, [this]() { m_defaultLanguage = V3LangCode::L1800_2023; }); - DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug DECL_OPTION("-no-threads", CbCall, [this, fl]() { fl->v3warn(DEPRECATED, "Option --no-threads is deprecated, use '--threads 1' instead"); m_threads = 1; @@ -1576,6 +1587,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_threads = 1; } }); + DECL_OPTION("-threads-coarsen", OnOff, &m_threadsCoarsen).undocumented(); // Debug DECL_OPTION("-threads-dpi", CbVal, [this, fl](const char* valp) { if (!std::strcmp(valp, "all")) { m_threadsDpiPure = true; @@ -1617,9 +1629,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } }); DECL_OPTION("-timing", OnOff, &m_timing); - DECL_OPTION("-top-module", Set, &m_topModule); DECL_OPTION("-top", Set, &m_topModule); - DECL_OPTION("-no-trace-top", Set, &m_noTraceTop); + DECL_OPTION("-top-module", Set, &m_topModule); DECL_OPTION("-trace", OnOff, &m_trace); DECL_OPTION("-trace-coverage", OnOff, &m_traceCoverage); DECL_OPTION("-trace-depth", Set, &m_traceDepth); @@ -1645,6 +1656,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_traceThreads = std::atoi(valp); if (m_traceThreads < 1) fl->v3fatal("--trace-threads must be >= 1: " << valp); }); + DECL_OPTION("-no-trace-top", Set, &m_noTraceTop); DECL_OPTION("-trace-underscore", OnOff, &m_traceUnderscore); DECL_OPTION("-U", CbPartialMatch, &V3PreShell::undef); @@ -1662,6 +1674,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, V3Options::addLibraryFile(parseFileArg(optdir, valp)); }); DECL_OPTION("-valgrind", CbCall, []() {}); // Processed only in bin/verilator shell + DECL_OPTION("-verilate", OnOff, &m_verilate); DECL_OPTION("-verilate-jobs", CbVal, [this, fl](const char* valp) { int val = std::atoi(valp); if (val < 0) { @@ -1673,17 +1686,12 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, } m_verilateJobs = val; }); - DECL_OPTION("-verilate", OnOff, &m_verilate); DECL_OPTION("-version", CbCall, [this]() { showVersion(false); std::exit(0); }); DECL_OPTION("-vpi", OnOff, &m_vpi); - DECL_OPTION("-Wpedantic", CbCall, [this]() { - m_pedantic = true; - V3Error::pretendError(V3ErrorCode::ASSIGNIN, false); - }); DECL_OPTION("-Wall", CbCall, []() { FileLine::globalWarnLintOff(false); FileLine::globalWarnStyleOff(false); @@ -1726,6 +1734,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-Wno-style", CbCall, []() { FileLine::globalWarnStyleOff(true); }); DECL_OPTION("-Wno-UNUSED", CbCall, []() { FileLine::globalWarnUnusedOff(true); }); DECL_OPTION("-Wno-WIDTH", CbCall, []() { FileLine::globalWarnOff(V3ErrorCode::WIDTH, true); }); + DECL_OPTION("-Wpedantic", CbCall, [this]() { + m_pedantic = true; + V3Error::pretendError(V3ErrorCode::ASSIGNIN, false); + }); DECL_OPTION("-Wwarn-", CbPartialMatch, [this, fl, &parser](const char* optp) VL_MT_DISABLED { const V3ErrorCode code{optp}; if (code == V3ErrorCode::EC_ERROR) { @@ -1795,19 +1807,11 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, m_xmlOutput = valp; m_xmlOnly = true; }); - DECL_OPTION("-json-only", OnOff, &m_jsonOnly); - DECL_OPTION("-json-only-output", CbVal, [this](const char* valp) { - m_jsonOnlyOutput = valp; - m_jsonOnly = true; - }); - DECL_OPTION("-json-only-meta-output", CbVal, [this](const char* valp) { - m_jsonOnlyMetaOutput = valp; - m_jsonOnly = true; - }); DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) { addIncDirUser(parseFileArg(optdir, string{valp})); }); + parser.finalize(); for (int i = 0; i < argc;) { diff --git a/src/V3Options.h b/src/V3Options.h index b19c6dfad..9606f7a42 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -255,6 +255,7 @@ private: bool m_flatten = false; // main switch: --flatten bool m_hierarchical = false; // main switch: --hierarchical bool m_ignc = false; // main switch: --ignc + bool m_jsonOnly = false; // main switch: --json-only bool m_lintOnly = false; // main switch: --lint-only bool m_gmake = false; // main switch: --make gmake bool m_main = false; // main switch: --main @@ -302,7 +303,6 @@ private: bool m_waiverMultiline = false; // main switch: --waiver-multiline bool m_xInitialEdge = false; // main switch: --x-initial-edge bool m_xmlOnly = false; // main switch: --xml-only - bool m_jsonOnly = false; // main switch: --json-only int m_buildJobs = -1; // main switch: --build-jobs, -j int m_convergeLimit = 100; // main switch: --converge-limit @@ -351,6 +351,8 @@ private: string m_exeName; // main switch: -o {name} string m_flags; // main switch: -f {name} string m_hierParamsFile; // main switch: --hierarchical-params-file + string m_jsonOnlyOutput; // main switch: --json-only-output + string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output string m_l2Name; // main switch: --l2name; "" for top-module's name string m_libCreate; // main switch: --lib-create {lib_name} string m_mainTopName; // main switch: --main-top-name @@ -365,8 +367,6 @@ private: string m_xAssign; // main switch: --x-assign string m_xInitial; // main switch: --x-initial string m_xmlOutput; // main switch: --xml-output - string m_jsonOnlyOutput; // main switch: --json-only-output - string m_jsonOnlyMetaOutput; // main switch: --json-only-meta-output // Language is now held in FileLine, on a per-node basis. However we still // have a concept of the default language at a global level. @@ -527,6 +527,7 @@ public: bool traceUnderscore() const { return m_traceUnderscore; } bool main() const { return m_main; } bool outFormatOk() const { return m_outFormatOk; } + bool jsonOnly() const { return m_jsonOnly; } bool keepTempFiles() const { return (V3Error::debugDefault() != 0); } bool pedantic() const { return m_pedantic; } bool pinsInoutEnables() const { return m_pinsInoutEnables; } @@ -557,7 +558,6 @@ public: bool waiverMultiline() const { return m_waiverMultiline; } bool xInitialEdge() const { return m_xInitialEdge; } bool xmlOnly() const { return m_xmlOnly; } - bool jsonOnly() const { return m_jsonOnly; } bool serializeOnly() const { return m_xmlOnly || m_jsonOnly; } bool topIfacesSupported() const { return lintOnly() && !hierarchical(); } @@ -617,6 +617,8 @@ public: string exeName() const { return m_exeName != "" ? m_exeName : prefix(); } string hierParamFile() const { return m_hierParamsFile; } + string jsonOnlyOutput() const { return m_jsonOnlyOutput; } + string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; } string l2Name() const { return m_l2Name; } string libCreate() const { return m_libCreate; } string libCreateName(bool shared) { @@ -644,8 +646,6 @@ public: string xAssign() const { return m_xAssign; } string xInitial() const { return m_xInitial; } string xmlOutput() const { return m_xmlOutput; } - string jsonOnlyOutput() const { return m_jsonOnlyOutput; } - string jsonOnlyMetaOutput() const { return m_jsonOnlyMetaOutput; } const V3StringSet& cppFiles() const { return m_cppFiles; } const V3StringList& cFlags() const { return m_cFlags; } From 694f2ff7c7fb0ec9bbe5e78abe3a571f12b3cbcd Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 10:24:59 -0500 Subject: [PATCH 51/93] Tests: renames --- test_regress/t/{t_optm_if_array.py => t_opt_if_array.py} | 0 test_regress/t/{t_optm_if_array.v => t_opt_if_array.v} | 0 test_regress/t/{t_optm_redor.py => t_opt_redor.py} | 0 test_regress/t/{t_optm_redor.v => t_opt_redor.v} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename test_regress/t/{t_optm_if_array.py => t_opt_if_array.py} (100%) rename test_regress/t/{t_optm_if_array.v => t_opt_if_array.v} (100%) rename test_regress/t/{t_optm_redor.py => t_opt_redor.py} (100%) rename test_regress/t/{t_optm_redor.v => t_opt_redor.v} (100%) diff --git a/test_regress/t/t_optm_if_array.py b/test_regress/t/t_opt_if_array.py similarity index 100% rename from test_regress/t/t_optm_if_array.py rename to test_regress/t/t_opt_if_array.py diff --git a/test_regress/t/t_optm_if_array.v b/test_regress/t/t_opt_if_array.v similarity index 100% rename from test_regress/t/t_optm_if_array.v rename to test_regress/t/t_opt_if_array.v diff --git a/test_regress/t/t_optm_redor.py b/test_regress/t/t_opt_redor.py similarity index 100% rename from test_regress/t/t_optm_redor.py rename to test_regress/t/t_opt_redor.py diff --git a/test_regress/t/t_optm_redor.v b/test_regress/t/t_opt_redor.v similarity index 100% rename from test_regress/t/t_optm_redor.v rename to test_regress/t/t_opt_redor.v From 05c7a3b90b7607ef4ae522b5ccec23d6ad308966 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 10:29:35 -0500 Subject: [PATCH 52/93] Optimize labels as final `if` block statements (#5744). --- Changes | 1 + src/V3Const.cpp | 32 ++++++++++----- test_regress/t/t_opt_ifjumpgo.py | 20 ++++++++++ test_regress/t/t_opt_ifjumpgo.v | 67 ++++++++++++++++++++++++++++++++ 4 files changed, 111 insertions(+), 9 deletions(-) create mode 100755 test_regress/t/t_opt_ifjumpgo.py create mode 100644 test_regress/t/t_opt_ifjumpgo.v diff --git a/Changes b/Changes index b16657146..a3badf96a 100644 --- a/Changes +++ b/Changes @@ -25,6 +25,7 @@ Verilator 5.033 devel * Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.] * Improve VPI write errors (#5712). [Andrew Nolte] * Improve `resetall support (#5728) (#5730). [Ethan Sifferman] +* Optimize labels as final `if` block statements (#5744). * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] diff --git a/src/V3Const.cpp b/src/V3Const.cpp index d39760288..782b7a00b 100644 --- a/src/V3Const.cpp +++ b/src/V3Const.cpp @@ -2266,6 +2266,19 @@ class ConstVisitor final : public VNVisitor { } return false; } + bool replaceJumpGoNext(AstJumpGo* nodep, AstNode* abovep) { + // If JumpGo has an upper JumpBlock that is to same label, then + // code will by normal sequential operation do the JUMPGO and it + // can be removed. + if (nodep->nextp()) return false; // Label jumps other statements + AstJumpBlock* const aboveBlockp = VN_CAST(abovep, JumpBlock); + if (!aboveBlockp) return false; + if (aboveBlockp != nodep->labelp()->blockp()) return false; + if (aboveBlockp->endStmtsp() != nodep->labelp()) return false; + UINFO(4, "JUMPGO => last remove " << nodep << endl); + VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); + return true; + } // Boolean replacements bool operandBoolShift(const AstNode* nodep) { @@ -3158,8 +3171,9 @@ class ConstVisitor final : public VNVisitor { nodep->condp(new AstLogAnd{lowerIfp->fileline(), condp, lowerCondp}); lowerIfp->replaceWith(lowerThensp); VL_DO_DANGLING(pushDeletep(lowerIfp), lowerIfp); - } else if (operandBoolShift(nodep->condp())) { - replaceBoolShift(nodep->condp()); + } else { + // Optimizations that don't reform the IF itself + if (operandBoolShift(nodep->condp())) replaceBoolShift(nodep->condp()); } } } @@ -3390,14 +3404,14 @@ class ConstVisitor final : public VNVisitor { } // If last statement in a jump label we have JumpLabel(...., JumpGo) // Often caused by "return" in a Verilog function. The Go is pointless, remove. + if (replaceJumpGoNext(nodep, nodep->abovep())) return; + // Also optimize If with a then or else's final statement being this JumpGo + // We only do single ifs... Ideally we'd look at control flow and delete any + // Jumps where any following control flow point is the label if (!nodep->nextp()) { - if (AstJumpBlock* const aboveBlockp = VN_CAST(nodep->abovep(), JumpBlock)) { - if (aboveBlockp == nodep->labelp()->blockp()) { - if (aboveBlockp->endStmtsp() == nodep->labelp()) { - UINFO(4, "JUMPGO => last remove " << nodep << endl); - VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep); - return; - } + if (AstNodeIf* const aboveIfp = VN_CAST(nodep->abovep(), NodeIf)) { + if (!aboveIfp->nextp()) { + if (replaceJumpGoNext(nodep, aboveIfp->abovep())) return; } } } diff --git a/test_regress/t/t_opt_ifjumpgo.py b/test_regress/t/t_opt_ifjumpgo.py new file mode 100755 index 000000000..2c0e4f4f6 --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--stats']) + +test.file_grep(test.stats, r'Node count, JUMPGO + 3 *$') + +test.execute() + +test.passes() diff --git a/test_regress/t/t_opt_ifjumpgo.v b/test_regress/t/t_opt_ifjumpgo.v new file mode 100644 index 000000000..e0fabacaa --- /dev/null +++ b/test_regress/t/t_opt_ifjumpgo.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +class uvm_object; +endclass + +class uvm_callback; +endclass + +class uvm_callbacks #(type T=uvm_object, type CB=uvm_callback); + bit m_registered = 1; + virtual function bit m_is_registered(uvm_object obj, uvm_callback cb); + if (m_is_for_me(cb) && m_am_i_a(obj)) begin + return m_registered; + end + endfunction + + virtual function bit m_is_for_me(uvm_callback cb); + CB this_cb; + // verilator lint_off WIDTHTRUNC + return ($cast(this_cb, cb)); + // verilator lint_on WIDTHTRUNC + endfunction + + virtual function bit m_am_i_a(uvm_object obj); + T this_t; + // verilator lint_off WIDTHTRUNC + return ($cast(this_t, obj)); + // verilator lint_on WIDTHTRUNC + endfunction +endclass + +class my_object extends uvm_object; +endclass + +class my_callback extends uvm_callback; +endclass + +class other_object extends uvm_object; +endclass + +module t; + + initial begin + my_object obj; + other_object oobj; + my_callback cb; + uvm_callbacks#(my_object, my_callback) ucs; + bit i; + + obj = new; + oobj = new; + cb = new; + ucs = new; + + i = ucs.m_is_registered(obj, cb); + if (i !== 1) $stop; + i = ucs.m_is_registered(oobj, cb); + if (i !== 0) $stop; + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From 98e40c847c2f7c0ccb545802119439eda59c8cdf Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 12:06:05 -0500 Subject: [PATCH 53/93] Internals: Suppress vlSelf unused suppression when known used. No functional change intended. --- src/V3EmitCFunc.h | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 2ffee3e8b..972970647 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -304,15 +304,6 @@ public: } puts(" {\n"); - if (nodep->isLoose()) { - m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration - if (!nodep->isStatic()) { // Standard prologue - m_useSelfForThis = true; - puts("(void)vlSelf; // Prevent unused variable warning\n"); - if (!VN_IS(m_modp, Class)) puts(symClassAssign()); - } - } - // "+" in the debug indicates a print from the model puts("VL_DEBUG_IF(VL_DBG_MSGF(\"+ "); for (int i = 0; i < m_modp->level(); ++i) puts(" "); @@ -320,6 +311,18 @@ public: puts(nodep->isLoose() ? "__" : "::"); puts(nodep->nameProtect() + "\\n\"); );\n"); + if (nodep->isLoose()) { + m_lazyDecls.declared(nodep); // Defined here, so no longer needs declaration + if (!nodep->isStatic()) { // Standard prologue + m_useSelfForThis = true; + if (!VN_IS(m_modp, Class)) { + puts(symClassAssign()); // Uses vlSelf + } else { + puts("(void)vlSelf; // Prevent unused variable warning\n"); + } + } + } + // Instantiate a process class if it's going to be needed somewhere later nodep->forall([&](const AstNodeCCall* ccallp) -> bool { if (ccallp->funcp()->needProcess() From 001c098e5a38582e572375ef9c91d437c943881c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 12:13:25 -0500 Subject: [PATCH 54/93] Optimize empty function definition bodies (#5750). --- Changes | 1 + src/V3AstNodeOther.h | 7 +++++-- src/V3AstNodes.cpp | 6 ++++-- src/V3CCtors.cpp | 2 ++ src/V3EmitCBase.cpp | 6 +++++- src/V3EmitCFunc.h | 1 + src/V3Sched.cpp | 1 + test_regress/t/t_timing_debug2.out | 22 ---------------------- 8 files changed, 19 insertions(+), 27 deletions(-) diff --git a/Changes b/Changes index a3badf96a..db6a674f5 100644 --- a/Changes +++ b/Changes @@ -26,6 +26,7 @@ Verilator 5.033 devel * Improve VPI write errors (#5712). [Andrew Nolte] * Improve `resetall support (#5728) (#5730). [Ethan Sifferman] * Optimize labels as final `if` block statements (#5744). +* Optimize empty function definition bodies (#5750). * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index e8b8c425f..91a1ab405 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -625,6 +625,7 @@ class AstCFunc final : public AstNode { bool m_isTrace : 1; // Function is related to tracing bool m_dontCombine : 1; // V3Combine shouldn't compare this func tree, it's special bool m_declPrivate : 1; // Declare it private + bool m_keepIfEmpty : 1; // Keep declaration and definition separate, even if empty bool m_slow : 1; // Slow routine, called once or just at init time bool m_funcPublic : 1; // From user public task/function bool m_isConstructor : 1; // Is C class constructor @@ -655,6 +656,7 @@ public: m_isTrace = false; m_dontCombine = false; m_declPrivate = false; + m_keepIfEmpty = false; m_slow = false; m_funcPublic = false; m_isConstructor = false; @@ -706,6 +708,8 @@ public: bool dontInline() const { return dontCombine() || slow() || funcPublic(); } bool declPrivate() const { return m_declPrivate; } void declPrivate(bool flag) { m_declPrivate = flag; } + bool keepIfEmpty() const VL_MT_SAFE { return m_keepIfEmpty; } + void keepIfEmpty(bool flag) { m_keepIfEmpty = flag; } bool slow() const VL_MT_SAFE { return m_slow; } void slow(bool flag) { m_slow = flag; } bool funcPublic() const { return m_funcPublic; } @@ -749,8 +753,7 @@ public: void cost(int cost) { m_cost = cost; } // Special methods bool emptyBody() const { - return argsp() == nullptr && initsp() == nullptr && stmtsp() == nullptr - && finalsp() == nullptr; + return !keepIfEmpty() && !argsp() && !initsp() && !stmtsp() && !finalsp(); } }; class AstCLocalScope final : public AstNode { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index f8b4a6f9b..ab9c98fe0 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2865,15 +2865,17 @@ void AstCFile::dumpJson(std::ostream& str) const { void AstCFunc::dump(std::ostream& str) const { this->AstNode::dump(str); if (slow()) str << " [SLOW]"; - if (dpiPure()) str << " [DPIPURE]"; if (isStatic()) str << " [STATIC]"; + if (dpiContext()) str << " [DPICTX]"; if (dpiExportDispatcher()) str << " [DPIED]"; if (dpiExportImpl()) str << " [DPIEI]"; if (dpiImportPrototype()) str << " [DPIIP]"; if (dpiImportWrapper()) str << " [DPIIW]"; - if (dpiContext()) str << " [DPICTX]"; + if (dpiPure()) str << " [DPIPURE]"; if (isConstructor()) str << " [CTOR]"; if (isDestructor()) str << " [DTOR]"; + if (isMethod()) str << " [METHOD]"; + if (isLoose()) str << " [LOOSE]"; if (isVirtual()) str << " [VIRT]"; if (isCoroutine()) str << " [CORO]"; if (needProcess()) str << " [NPRC]"; diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 045628e6a..30b5d5f69 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -62,6 +62,7 @@ class V3CCtorsBuilder final { AstCFunc* const funcp = new AstCFunc{m_modp->fileline(), funcName, nullptr, "void"}; funcp->isStatic(false); funcp->isLoose(!m_type.isClass()); + funcp->keepIfEmpty(true); // TODO relax funcp->declPrivate(true); funcp->slow(!m_type.isClass()); // Only classes construct on fast path string preventUnusedStmt; @@ -210,6 +211,7 @@ void V3CCtors::evalAsserts() { funcp->declPrivate(true); funcp->isStatic(false); funcp->isLoose(true); + funcp->keepIfEmpty(true); funcp->slow(false); funcp->ifdef("VL_DEBUG"); modp->addStmtsp(funcp); diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 547a17884..2cb45c20b 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -171,7 +171,11 @@ void EmitCBaseVisitorConst::emitCFuncDecl(const AstCFunc* funcp, const AstNodeMo putns(funcp, "virtual "); } emitCFuncHeader(funcp, modp, /* withScope: */ false); - putns(funcp, ";\n"); + if (funcp->emptyBody() && !funcp->isLoose() && !cLinkage) { + putns(funcp, " {}\n"); + } else { + putns(funcp, ";\n"); + } if (!funcp->ifdef().empty()) putns(funcp, "#endif // " + funcp->ifdef() + "\n"); } diff --git a/src/V3EmitCFunc.h b/src/V3EmitCFunc.h index 972970647..00374ae1f 100644 --- a/src/V3EmitCFunc.h +++ b/src/V3EmitCFunc.h @@ -280,6 +280,7 @@ public: // VISITORS using EmitCConstInit::visit; void visit(AstCFunc* nodep) override { + if (nodep->emptyBody() && !nodep->isLoose()) return; VL_RESTORER(m_useSelfForThis); VL_RESTORER(m_cfuncp); VL_RESTORER(m_instantiatesOwnProcess) diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 61a7f21d1..76f6cfbdc 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -70,6 +70,7 @@ AstCFunc* makeSubFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* makeTopFunction(AstNetlist* netlistp, const string& name, bool slow) { AstCFunc* const funcp = makeSubFunction(netlistp, name, slow); funcp->entryPoint(true); + funcp->keepIfEmpty(true); return funcp; } diff --git a/test_regress/t/t_timing_debug2.out b/test_regress/t/t_timing_debug2.out index ac4dd1943..a00833c4e 100644 --- a/test_regress/t/t_timing_debug2.out +++ b/test_regress/t/t_timing_debug2.out @@ -185,7 +185,6 @@ -V{t#,#} Awaiting time 101: Process waiting at t/t_timing_class.v:274 -V{t#,#} Resuming delayed processes -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:173 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:247 -V{t#,#} Process forked at t/t_timing_class.v:246 finished @@ -499,7 +498,6 @@ -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:257 -V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:174 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_delay -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:131 -V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::__VnoInFunc_flip @@ -1038,9 +1036,7 @@ -V{t#,#} Process forked at t/t_timing_class.v:250 finished -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:245 -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:175 --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_delay --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::__VnoInFunc_do_sth_else -V{t#,#}+ Vt_timing_debug2_t___eval_initial__TOP__t__Vtiming__6____Vfork_1__0 -V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::__VnoInFunc_do_assign -V{t#,#} Resuming: Process waiting at t/t_timing_class.v:76 @@ -1082,8 +1078,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aForkClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1236,7 +1230,6 @@ -V{t#,#}End-of-eval cleanup -V{t#,#}+++++TOP Evaluate Vt_timing_debug2::eval_step -V{t#,#}+ Vt_timing_debug2___024root___eval_debug_assertions --V{t#,#}+ Vt_timing_debug2_t__03a__03aAssignDelayClass::~ -V{t#,#}+ Eval -V{t#,#}+ Vt_timing_debug2___024root___eval -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__act @@ -1308,18 +1301,3 @@ -V{t#,#}+ Vt_timing_debug2___024root___eval_phase__nba -V{t#,#}End-of-eval cleanup -V{t#,#}+ Vt_timing_debug2___024root___eval_final --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay40::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay20::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelay10::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aNoDelay::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aDelayClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aClkClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aLocalWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aWaitClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ --V{t#,#}+ Vt_timing_debug2_t__03a__03aEventClass::~ --V{t#,#}+ Vt_timing_debug2___024unit__03a__03aBaseClass::~ From 41bb54bc2c47d2abc0ee41015b6e584ac50917d0 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 13:35:56 -0500 Subject: [PATCH 55/93] Add Makefile targets count to stats --- src/V3EmitMk.cpp | 17 +++++++++++++---- test_regress/t/t_flag_csplit_groups.py | 6 ++++++ 2 files changed, 19 insertions(+), 4 deletions(-) diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index f87463f34..4ffd6a29f 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -479,6 +479,9 @@ class EmitMk final { using FileOrConcatenatedFilesList = EmitGroup::FileOrConcatenatedFilesList; using FilenameWithScore = EmitGroup::FilenameWithScore; + // MEMBERS + double m_putClassCount = 0; // Number of classEntries printed + public: // METHODS @@ -494,6 +497,7 @@ public: void putMakeClassEntry(V3OutMkFile& of, const string& name) { of.puts("\t" + V3Os::filenameNonDirExt(name) + " \\\n"); + ++m_putClassCount; } void emitClassMake() { @@ -577,9 +581,12 @@ public: } else { of.puts(", fast-path, compile with highest optimization\n"); } - of.puts(support == 2 ? "VM_GLOBAL" : support == 1 ? "VM_SUPPORT" : "VM_CLASSES"); - of.puts(slow ? "_SLOW" : "_FAST"); - of.puts(" += \\\n"); + const string targetVar = (support == 2 ? "VM_GLOBAL"s + : support == 1 ? "VM_SUPPORT"s + : "VM_CLASSES"s) + + (slow ? "_SLOW" : "_FAST"); + m_putClassCount = 0; + of.puts(targetVar + " += \\\n"); if (support == 2 && v3Global.opt.hierChild()) { // Do nothing because VM_GLOBAL is necessary per executable. Top module will // have them. @@ -605,7 +612,7 @@ public: const std::vector& list = slow ? vmClassesSlowList : vmClassesFastList; for (const FileOrConcatenatedFilesList& entry : list) { - if (entry.isConcatenatingFile()) { emitConcatenatingFile(entry); } + if (entry.isConcatenatingFile()) emitConcatenatingFile(entry); putMakeClassEntry(of, entry.m_filename); } } else { @@ -619,6 +626,7 @@ public: } } of.puts("\n"); + V3Stats::addStat("Makefile targets, " + targetVar, m_putClassCount); } } @@ -808,6 +816,7 @@ public: }; //###################################################################### + class EmitMkHierVerilation final { const V3HierBlockPlan* const m_planp; const string m_makefile; // path of this makefile diff --git a/test_regress/t/t_flag_csplit_groups.py b/test_regress/t/t_flag_csplit_groups.py index 8fe9707ba..467fe6ad4 100755 --- a/test_regress/t/t_flag_csplit_groups.py +++ b/test_regress/t/t_flag_csplit_groups.py @@ -87,6 +87,7 @@ test.compile(v_flags2=["--trace", "--output-groups 2", "--output-split-cfuncs 1", "--exe", + "--stats", "../" + test.main_filename], verilator_make_gmake=False) # yapf:disable @@ -122,4 +123,9 @@ test.file_grep(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_Slow_2") test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") +# Check combine count +test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (174 if test.vltmt else 156)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', (2 if test.vltmt else 3)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2) + test.passes() From 70c6651aac75ce35c8fb757f6eff94a4d4c5db8c Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 15:38:27 -0500 Subject: [PATCH 56/93] Fix -Wno-subobject-linkage on GCC (#5754) --- configure.ac | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/configure.ac b/configure.ac index 4420a1f7f..4d815fc0b 100644 --- a/configure.ac +++ b/configure.ac @@ -478,6 +478,7 @@ m4_foreach([cflag],[ [-Wno-parentheses-equality], [-Wno-shadow], [-Wno-sign-compare], + [-Wno-subobject-linkage], [-Wno-tautological-bitwise-compare], [-Wno-tautological-compare], [-Wno-uninitialized], @@ -538,7 +539,7 @@ _MY_LDLIBS_CHECK_IFELSE( [if test "$CFG_WITH_TCMALLOC" != "no"; then CFG_LIBS="$LTCMALLOC $CFG_LIBS"; # If using tcmalloc, add some extra options to make the compiler not assume - # it is using it's own versions of the standard library functions + # it is using its own versions of the standard library functions _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-malloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-calloc) _MY_CXX_CHECK_OPT(CFG_CXXFLAGS_SRC,-fno-builtin-realloc) From bdcfc4e286390b6d6a7e56ff8f747bc1a7df75f8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 16:01:10 -0500 Subject: [PATCH 57/93] Reduce verbosity under `--debug` default level --- src/V3Error.cpp | 2 +- src/V3Unroll.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/V3Error.cpp b/src/V3Error.cpp index 23c70a013..e9ad855cb 100644 --- a/src/V3Error.cpp +++ b/src/V3Error.cpp @@ -154,7 +154,7 @@ void V3ErrorGuarded::v3errorEnd(std::ostringstream& sstr, const string& extra) } // Suppress duplicate messages if (!m_messages.insert(msg).second) return; - if (!extra.empty()) { + if (!extra.empty() && !m_errorSuppressed) { const string extraMsg = warnMore() + extra + "\n"; const size_t pos = msg.find('\n'); msg.insert(pos + 1, extraMsg); diff --git a/src/V3Unroll.cpp b/src/V3Unroll.cpp index ac707874b..d36083c5a 100644 --- a/src/V3Unroll.cpp +++ b/src/V3Unroll.cpp @@ -57,7 +57,7 @@ class UnrollVisitor final : public VNVisitor { bool cantUnroll(AstNode* nodep, const char* reason) const { if (m_generate) nodep->v3warn(E_UNSUPPORTED, "Unsupported: Can't unroll generate for; " << reason); - UINFO(3, " Can't Unroll: " << reason << " :" << nodep << endl); + UINFO(4, " Can't Unroll: " << reason << " :" << nodep << endl); // if (debug() >= 9) nodep->dumpTree("- cant: "); V3Stats::addStatSum("Unrolling gave up, "s + reason, 1); return false; @@ -210,7 +210,7 @@ class UnrollVisitor final : public VNVisitor { SimulateVisitor simvis; simvis.mainParamEmulate(clonep); if (!simvis.optimizable()) { - UINFO(3, "Unable to simulate" << endl); + UINFO(4, "Unable to simulate" << endl); if (debug() >= 9) nodep->dumpTree("- _simtree: "); VL_DO_DANGLING(clonep->deleteTree(), clonep); return false; From 61bfb6c308eac6792e527e552e0efb539885c5a4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 16:08:15 -0500 Subject: [PATCH 58/93] Remove `list`/`map` from keyword warnings. Reduces warning verbosity from `map` in UVM. Assumes user doesn't do a `using namespace std`. --- src/V3LanguageWords.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/V3LanguageWords.h b/src/V3LanguageWords.h index ba0f302de..c04603efa 100644 --- a/src/V3LanguageWords.h +++ b/src/V3LanguageWords.h @@ -56,7 +56,6 @@ private: inline void V3LanguageWords::Singleton::init() { // C++ keywords // clang-format off - addKwd("nullptr", "C++ common word"); addKwd("abort", "C++ common word"); addKwd("alignas", "C++11 keyword"); addKwd("alignof", "C++11 keyword"); @@ -91,7 +90,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("decltype", "C++11 keyword"); addKwd("default", "C++ keyword"); addKwd("delete", "C++ keyword"); - addKwd("deque", "C++ common word"); addKwd("do", "C++ keyword"); addKwd("double", "C++ keyword"); addKwd("dynamic_cast", "C++ keyword"); @@ -113,12 +111,8 @@ inline void V3LanguageWords::Singleton::init() { addKwd("int", "C++ keyword"); addKwd("interrupt", "C++ common word"); addKwd("iterator", "C++ common word"); - addKwd("list", "C++ common word"); addKwd("long", "C++ keyword"); - addKwd("map", "C++ common word"); addKwd("module", "C++ modules TS keyword"); - addKwd("std::multimap", "C++ common word"); - addKwd("std::multiset", "C++ common word"); addKwd("mutable", "C++ keyword"); addKwd("namespace", "C++ keyword"); addKwd("near", "C++ common word"); @@ -126,6 +120,7 @@ inline void V3LanguageWords::Singleton::init() { addKwd("noexcept", "C++11 keyword"); addKwd("not", "C++ keyword"); addKwd("not_eq", "C++ keyword"); + addKwd("nullptr", "C++ common word"); addKwd("nullptr", "C++11 keyword"); addKwd("operator", "C++ keyword"); addKwd("or", "C++ keyword"); @@ -142,7 +137,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("requires", "C++20 keyword"); addKwd("restrict", "C++ keyword"); addKwd("return", "C++ keyword"); - addKwd("set", "C++ common word"); addKwd("short", "C++ keyword"); addKwd("signed", "C++ keyword"); addKwd("sizeof", "C++ keyword"); @@ -150,6 +144,13 @@ inline void V3LanguageWords::Singleton::init() { addKwd("static", "C++ keyword"); addKwd("static_assert", "C++11 keyword"); addKwd("static_cast", "C++ keyword"); + addKwd("std::deque", "C++ common word"); + addKwd("std::list", "C++ common word"); + addKwd("std::map", "C++ common word"); + addKwd("std::multimap", "C++ common word"); + addKwd("std::multiset", "C++ common word"); + addKwd("std::set", "C++ common word"); + addKwd("std::vector", "C++ common word"); addKwd("struct", "C++ keyword"); addKwd("switch", "C++ keyword"); addKwd("synchronized", "C++ TM TS keyword"); @@ -171,7 +172,6 @@ inline void V3LanguageWords::Singleton::init() { addKwd("union", "C++ keyword"); addKwd("unsigned", "C++ keyword"); addKwd("using", "C++ keyword"); - addKwd("vector", "C++ common word"); addKwd("virtual", "C++ keyword"); addKwd("void", "C++ keyword"); addKwd("volatile", "C++ keyword"); From e2a6e19cacd31557bcbcd75588a04640d1411be8 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 25 Jan 2025 17:52:27 -0500 Subject: [PATCH 59/93] Cleanup V3EmitMk report style --- src/V3EmitMk.cpp | 50 +++++++++++++++++++++++------------------------- src/V3Os.cpp | 4 ++-- 2 files changed, 26 insertions(+), 28 deletions(-) diff --git a/src/V3EmitMk.cpp b/src/V3EmitMk.cpp index 4ffd6a29f..71d1979a6 100644 --- a/src/V3EmitMk.cpp +++ b/src/V3EmitMk.cpp @@ -96,7 +96,7 @@ private: std::sort(sortedScores.begin(), sortedScores.end()); const int64_t topScore = sortedScores.back(); - os << "Top score: " << topScore << endl; + os << "Top score: " << topScore << '\n'; const int maxScoreWidth = std::to_string(topScore).length(); const int64_t intervalsNum = std::min(topScore + 1, MAX_INTERVALS_NUM); @@ -123,7 +123,7 @@ private: for (const Interval& iv : intervals) topIntervalSize = std::max(topIntervalSize, iv.m_size); - os << "Input files' scores histogram:" << endl; + os << "Input files' scores histogram:\n"; for (const Interval& iv : intervals) { const int scaledSize = iv.m_size * (MAX_BAR_LENGTH + 1) / topIntervalSize; @@ -132,14 +132,14 @@ private: os << std::setw(maxScoreWidth) << iv.m_lowerBound << line << " " << iv.m_size << '\n'; } os << std::setw(maxScoreWidth) << (topScore + 1) << '\n'; - os << endl; + os << '\n'; } // PRIVATE METHODS // Debug logging: dumps Work Lists and their lists of files void dumpWorkLists(std::ostream& os) { - os << "Initial Work Lists with their concatenation eligibility status:" << endl; + os << "Initial Work Lists with their concatenation eligibility status:\n"; for (const WorkList& list : m_workLists) { os << "+ [" << (list.m_isConcatenable ? 'x' : ' ') << "] Work List #" << list.m_dbgId << " (num of files: " << list.m_files.size() @@ -157,13 +157,13 @@ private: os << "| + " << last.m_filename << " (score: " << last.m_score << ")\n"; } } - os << endl; + os << '\n'; } // Debug logging: dumps list of output files. List of grouped files is additionally printed // for each concatenating file. void dumpOutputList(std::ostream& os) const { - os << "List of output files after execution of concatenation:" << endl; + os << "List of output files after execution of concatenation:\n"; for (const FileOrConcatenatedFilesList& entry : m_outputFiles) { if (entry.isConcatenatingFile()) { @@ -182,15 +182,16 @@ private: const int totalBucketsNum = v3Global.opt.outputGroups(); // Return early if there's nothing to do. bool groupingRedundant = false; - if (inputFilesCount < MIN_FILES_COUNT) { - UINFO(4, "File concatenation skipped: Too few files (" - << m_inputFiles.size() << " < " << MIN_FILES_COUNT << ")" << endl); + if (inputFilesCount < MIN_FILES_COUNT + && inputFilesCount <= static_cast(totalBucketsNum)) { + UINFO(4, "File concatenation skipped: Too few files (" << m_inputFiles.size() << " < " + << MIN_FILES_COUNT << ")\n"); groupingRedundant = true; } if (inputFilesCount < (MIN_FILES_PER_BUCKET * totalBucketsNum)) { UINFO(4, "File concatenation skipped: Too few files per bucket (" << m_inputFiles.size() << " < " << MIN_FILES_PER_BUCKET << " - " - << totalBucketsNum << ")" << endl); + << totalBucketsNum << ")\n"); groupingRedundant = true; } if (!groupingRedundant) return false; @@ -212,13 +213,12 @@ private: V3Stats::addStat("Concatenation max score", concatenableFileMaxScore); int nextWorkListId = 0; - if (m_logp) *m_logp << "Input files with their concatenation eligibility status:" << endl; + if (m_logp) *m_logp << "Input files with their concatenation eligibility status:\n"; for (const FilenameWithScore& inputFile : m_inputFiles) { const bool fileIsConcatenable = (inputFile.m_score <= concatenableFileMaxScore); if (m_logp) *m_logp << " + [" << (fileIsConcatenable ? 'x' : ' ') << "] " - << inputFile.m_filename << " (score: " << inputFile.m_score << ")" - << endl; + << inputFile.m_filename << " (score: " << inputFile.m_score << ")\n"; V3Stats::addStatSum(fileIsConcatenable ? "Concatenation total grouped score" : "Concatenation total non-grouped score", inputFile.m_score); @@ -233,7 +233,7 @@ private: list.m_files.push_back({inputFile.m_filename, inputFile.m_score}); list.m_totalScore += inputFile.m_score; } - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; } void assignBuckets(uint64_t concatenableFilesTotalScore) { @@ -248,8 +248,7 @@ private: // Debugging: Log which work lists will be kept if (m_logp) { *m_logp << "More Work Lists than buckets; " - "Work Lists with statuses indicating whether the list will be kept:" - << endl; + "Work Lists with statuses indicating whether the list will be kept:\n"; // Only lists that will be kept. List that will be removed are logged below. std::for_each(m_concatenableListsByDescSize.begin(), m_concatenableListsByDescSize.begin() + totalBucketsNum, @@ -269,7 +268,7 @@ private: << " (num of files: " << listp->m_files.size() << "; total score: " << listp->m_totalScore << ")\n"; }); - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; m_concatenableListsByDescSize.resize(totalBucketsNum); // Recalculate stats @@ -283,7 +282,7 @@ private: V3Stats::addStat("Concatenation ideal bucket score", idealBucketScore); - if (m_logp) *m_logp << "Buckets assigned to Work Lists:" << endl; + if (m_logp) *m_logp << "Buckets assigned to Work Lists:\n"; int availableBuckets = v3Global.opt.outputGroups(); for (WorkList* listp : m_concatenableListsByDescSize) { if (availableBuckets > 0) { @@ -301,7 +300,7 @@ private: << std::right << " (excluding from concatenation)\n"; } } - if (m_logp) *m_logp << endl; + if (m_logp) *m_logp << '\n'; } void buildOutputList() { @@ -395,19 +394,19 @@ private: } void process() { - UINFO(4, __FUNCTION__ << ":" << endl); - UINFO(5, "Number of input files: " << m_inputFiles.size() << endl); - UINFO(5, "Total score: " << m_totalScore << endl); - UINFO(5, "Group file prefix: " << m_groupFilePrefix << endl); + UINFO(4, __FUNCTION__ << " group file prefix: " << m_groupFilePrefix << '\n'); + UINFO(5, "Number of input files: " << m_inputFiles.size() << '\n'); + UINFO(5, "Total score: " << m_totalScore << '\n'); const int totalBucketsNum = v3Global.opt.outputGroups(); - UINFO(5, "Number of buckets: " << totalBucketsNum << endl); + UINFO(5, "Number of buckets: " << totalBucketsNum << '\n'); UASSERT(totalBucketsNum > 0, "More than 0 buckets required"); if (fallbackNoGrouping(m_inputFiles.size())) return; - if (dumpLevel() >= 6) { + if (debug() >= 6 || dumpLevel() >= 6) { const string filename = v3Global.debugFilename("outputgroup") + ".txt"; + UINFO(5, "Dumping " << filename << endl); m_logp = std::unique_ptr{V3File::new_ofstream(filename)}; if (m_logp->fail()) v3fatal("Can't write " << filename); } @@ -417,7 +416,6 @@ private: createWorkLists(); // Collect stats and mark lists with only one file as non-concatenable - size_t concatenableFilesCount = 0; int64_t concatenableFilesTotalScore = 0; diff --git a/src/V3Os.cpp b/src/V3Os.cpp index cd38daf44..342c01f85 100644 --- a/src/V3Os.cpp +++ b/src/V3Os.cpp @@ -91,9 +91,9 @@ string V3Os::getenvStr(const string& envvar, const string& defaultValue) { void V3Os::setenvStr(const string& envvar, const string& value, const string& why) { if (why != "") { - UINFO(1, "export " << envvar << "=" << value << " # " << why << endl); + UINFO(1, "export " << envvar << "='" << value << "' # " << why << endl); } else { - UINFO(1, "export " << envvar << "=" << value << endl); + UINFO(1, "export " << envvar << "='" << value << "'" << endl); } #if defined(_WIN32) || defined(__MINGW32__) _putenv_s(envvar.c_str(), value.c_str()); From a379382d32b1b52f8490c4d0ddf0d186f8c4d1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bart=C5=82omiej=20Chmiel?= Date: Tue, 28 Jan 2025 13:30:40 +0100 Subject: [PATCH 60/93] Fix handling forced assigns in V3Life (#5757) --- src/V3Life.cpp | 4 ++-- test_regress/t/t_force_assign.py | 18 ++++++++++++++++++ test_regress/t/t_force_assign.v | 24 ++++++++++++++++++++++++ 3 files changed, 44 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_force_assign.py create mode 100644 test_regress/t/t_force_assign.v diff --git a/src/V3Life.cpp b/src/V3Life.cpp index 47a7468b8..556fc23c0 100644 --- a/src/V3Life.cpp +++ b/src/V3Life.cpp @@ -287,8 +287,8 @@ class LifeVisitor final : public VNVisitor { } } void visit(AstNodeAssign* nodep) override { - if (nodep->isTimingControl()) { - // V3Life doesn't understand time sense - don't optimize + if (nodep->isTimingControl() || VN_IS(nodep, AssignForce)) { + // V3Life doesn't understand time sense nor force assigns - don't optimize setNoopt(); iterateChildren(nodep); return; diff --git a/test_regress/t/t_force_assign.py b/test_regress/t/t_force_assign.py new file mode 100755 index 000000000..f989a35fb --- /dev/null +++ b/test_regress/t/t_force_assign.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_force_assign.v b/test_regress/t/t_force_assign.v new file mode 100644 index 000000000..7b3724dd5 --- /dev/null +++ b/test_regress/t/t_force_assign.v @@ -0,0 +1,24 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +module t; + reg [2:0] a = 0; + + initial begin + a = 1; + if (a != 1) $stop; + + force a = 2; + if (a != 2) $stop; + + a = 3; + if (a != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From e0e164cea216e09a3de9c331b0b3c4180e5fd6ee Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Thu, 30 Jan 2025 07:52:38 -0500 Subject: [PATCH 61/93] Internals: Optimize clearFired (#5753) --- src/V3SenExprBuilder.h | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/src/V3SenExprBuilder.h b/src/V3SenExprBuilder.h index f837a960c..0149a2a56 100644 --- a/src/V3SenExprBuilder.h +++ b/src/V3SenExprBuilder.h @@ -182,16 +182,11 @@ class SenExprBuilder final { case VEdgeType::ET_EVENT: { UASSERT_OBJ(v3Global.hasEvents(), senItemp, "Inconsistent"); { - // If the event is fired, set up the clearing process - AstCMethodHard* const callp = new AstCMethodHard{flp, currp(), "isFired"}; - callp->dtypeSetBit(); - AstIf* const ifp = new AstIf{flp, callp}; - m_postUpdates.push_back(ifp); - // Clear 'fired' state when done + // No need to check if the event was fired, we need the flag clear regardless AstCMethodHard* const clearp = new AstCMethodHard{flp, currp(), "clearFired"}; clearp->dtypeSetVoid(); - ifp->addThensp(clearp->makeStmt()); + m_postUpdates.push_back(clearp->makeStmt()); } // Get 'fired' state From 6b4183632cef534bd9e6bff7a91104825bf3b98d Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Mon, 3 Feb 2025 17:56:00 +0100 Subject: [PATCH 62/93] Support Unpacked Structures' Constrained Randomization (#5657) (#5759) --- include/verilated.h | 3 + include/verilated_random.cpp | 2 +- include/verilated_random.h | 35 ++++++++--- src/V3AstNodeDType.h | 17 +++++- src/V3AstNodes.cpp | 14 +++++ src/V3EmitCHeaders.cpp | 39 ++++++++++++ src/V3Randomize.cpp | 25 ++++++++ test_regress/t/t_constraint_struct.py | 21 +++++++ test_regress/t/t_constraint_struct.v | 87 +++++++++++++++++++++++++++ test_regress/t/t_json_only_tag.out | 8 +-- 10 files changed, 235 insertions(+), 16 deletions(-) create mode 100755 test_regress/t/t_constraint_struct.py create mode 100755 test_regress/t/t_constraint_struct.v diff --git a/include/verilated.h b/include/verilated.h index baccd0610..d832eb7ab 100644 --- a/include/verilated.h +++ b/include/verilated.h @@ -175,6 +175,9 @@ enum class VerilatedAssertDirectiveType : uint8_t { using VerilatedAssertType_t = std::underlying_type::type; using VerilatedAssertDirectiveType_t = std::underlying_type::type; +// Type trait for custom struct +template +struct VlIsCustomStruct : public std::false_type {}; //============================================================================= // Utility functions diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index a525df5c4..7e048e6c1 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -508,7 +508,7 @@ void VlRandomizer::clear() { m_constraints.clear(); } #ifdef VL_DEBUG void VlRandomizer::dump() const { for (const auto& var : m_vars) { - VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name()); + VL_PRINTF("Variable (%d): %s\n", var.second->width(), var.second->name().c_str()); } for (const std::string& c : m_constraints) VL_PRINTF("Constraint: %s\n", c.c_str()); } diff --git a/include/verilated_random.h b/include/verilated_random.h index 24ba84385..7b1eaabbb 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -54,21 +54,22 @@ public: using ArrayInfoMap = std::map>; class VlRandomVar VL_NOT_FINAL { - const char* const m_name; // Variable name + std::string m_name; // Variable name void* const m_datap; // Reference to variable data const int m_width; // Variable width in bits const int m_dimension; //Variable dimension, default is 0 const std::uint32_t m_randModeIdx; // rand_mode index public: - VlRandomVar(const char* name, int width, void* datap, int dimension, std::uint32_t randModeIdx) + VlRandomVar(const std::string& name, int width, void* datap, int dimension, + std::uint32_t randModeIdx) : m_name{name} , m_datap{datap} , m_width{width} , m_dimension{dimension} , m_randModeIdx{randModeIdx} {} virtual ~VlRandomVar() = default; - const char* name() const { return m_name; } + std::string name() const { return m_name; } int width() const { return m_width; } int dimension() const { return m_dimension; } virtual void* datap(int idx) const { return m_datap; } @@ -99,7 +100,7 @@ public: template class VlRandomArrayVarTemplate final : public VlRandomVar { public: - VlRandomArrayVarTemplate(const char* name, int width, void* datap, int dimension, + VlRandomArrayVarTemplate(const std::string& name, int width, void* datap, int dimension, std::uint32_t randModeIdx) : VlRandomVar{name, width, datap, dimension, randModeIdx} {} void* datap(int idx) const override { @@ -300,8 +301,9 @@ public: } template - void write_var(T& var, int width, const char* name, int dimension, - std::uint32_t randmodeIdx = std::numeric_limits::max()) { + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { if (m_vars.find(name) != m_vars.end()) return; // TODO: make_unique once VlRandomizer is per-instance not per-ref m_vars[name] @@ -341,6 +343,22 @@ public: record_arr_table(var, name, dimension, {}, {}); } } + template + void modifyMembers(T& obj, std::index_sequence, std::string baseName) { + // Use the indices to access each member via std::get + (void)std::initializer_list{ + (write_var(std::get(obj.getMembers(obj)), + sizeof(std::get(obj.getMembers(obj))) * 8, + (baseName + "." + obj.memberNames()[I]).c_str(), 0), + 0)...}; + } + + template + typename std::enable_if::value, void>::type + write_var(T& var, int width, const char* name, int dimension, + std::uint32_t randmodeIdx = std::numeric_limits::max()) { + modifyMembers(var, var.memberIndices(), name); + } int idx; std::string generateKey(const std::string& name, int idx) { @@ -355,8 +373,9 @@ public: } template - void record_arr_table(T& var, const std::string name, int dimension, - std::vector indices, std::vector idxWidths) { + typename std::enable_if::value, void>::type + record_arr_table(T& var, const std::string name, int dimension, std::vector indices, + std::vector idxWidths) { const std::string key = generateKey(name, idx); m_arr_vars[key] = std::make_shared(name, &var, idx, indices, idxWidths); ++idx; diff --git a/src/V3AstNodeDType.h b/src/V3AstNodeDType.h index 83b80717f..1cc96fdbe 100644 --- a/src/V3AstNodeDType.h +++ b/src/V3AstNodeDType.h @@ -230,6 +230,7 @@ class AstNodeUOrStructDType VL_NOT_FINAL : public AstNodeDType { const int m_uniqueNum; bool m_packed; bool m_isFourstate = false; // V3Width computes + bool m_constrainedRand = false; // True if struct has constraint expression protected: AstNodeUOrStructDType(VNType t, FileLine* fl, VSigning numericUnpack) @@ -244,7 +245,8 @@ protected: , m_name(other.m_name) , m_uniqueNum(uniqueNumInc()) , m_packed(other.m_packed) - , m_isFourstate(other.m_isFourstate) {} + , m_isFourstate(other.m_isFourstate) + , m_constrainedRand(false) {} public: ASTGEN_MEMBERS_AstNodeUOrStructDType; @@ -284,6 +286,8 @@ public: VNumRange declRange() const VL_MT_STABLE { return VNumRange{hi(), lo()}; } AstNodeModule* classOrPackagep() const { return m_classOrPackagep; } void classOrPackagep(AstNodeModule* classpackagep) { m_classOrPackagep = classpackagep; } + bool isConstrainedRand() { return m_constrainedRand; } + void markConstrainedRand(bool flag) { m_constrainedRand = flag; } }; // === Concrete node types ===================================================== @@ -905,12 +909,14 @@ class AstMemberDType final : public AstNodeDType { string m_name; // Name of variable string m_tag; // Holds the string of the verilator tag -- used in XML output. int m_lsb = -1; // Within this level's packed struct, the LSB of the first bit of the member + bool m_constrainedRand = false; // UNSUP: int m_randType; // Randomization type (IEEE) public: AstMemberDType(FileLine* fl, const string& name, VFlagChildDType, AstNodeDType* dtp, AstNode* valuep) : ASTGEN_SUPER_MemberDType(fl) - , m_name{name} { + , m_name{name} + , m_constrainedRand(false) { childDTypep(dtp); // Only for parser this->valuep(valuep); dtypep(nullptr); // V3Width will resolve @@ -918,13 +924,16 @@ public: } AstMemberDType(FileLine* fl, const string& name, AstNodeDType* dtp) : ASTGEN_SUPER_MemberDType(fl) - , m_name{name} { + , m_name{name} + , m_constrainedRand(false) { UASSERT(dtp, "AstMember created with no dtype"); refDTypep(dtp); dtypep(this); widthFromSub(subDTypep()); } ASTGEN_MEMBERS_AstMemberDType; + void dump(std::ostream& str = std::cout) const override; + void dumpJson(std::ostream& str = std::cout) const override; void dumpSmall(std::ostream& str) const override; string name() const override VL_MT_STABLE { return m_name; } // * = Var name bool hasDType() const override VL_MT_SAFE { return true; } @@ -958,6 +967,8 @@ public: v3fatalSrc("call isCompound on subdata type, not reference"); return false; } + bool isConstrainedRand() const { return m_constrainedRand; } + void markConstrainedRand(bool flag) { m_constrainedRand = flag; } }; class AstNBACommitQueueDType final : public AstNodeDType { // @astgen ptr := m_subDTypep : AstNodeDType // Type of the corresponding variable diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index ab9c98fe0..f0ee72c84 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1950,6 +1950,20 @@ void AstJumpLabel::dump(std::ostream& str) const { } void AstJumpLabel::dumpJson(std::ostream& str) const { dumpJsonGen(str); } +void AstMemberDType::dump(std::ostream& str) const { + this->AstNodeDType::dump(str); + if (isConstrainedRand()) str << " [CONSTRAINEDRAND]"; + if (name() != "") str << " name=" << name(); + if (tag() != "") str << " tag=" << tag(); +} + +void AstMemberDType::dumpJson(std::ostream& str) const { + dumpJsonBoolFunc(str, isConstrainedRand); + dumpJsonStrFunc(str, name); + dumpJsonStrFunc(str, tag); + dumpJsonGen(str); +} + void AstMemberDType::dumpSmall(std::ostream& str) const { this->AstNodeDType::dumpSmall(str); str << "member"; diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 9af1c604d..41c471f0a 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -248,7 +248,43 @@ class EmitCHeader final : public EmitCConstInit { putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false)); puts(";\n"); } + // Three helper functions for struct constrained randomization: + // - memberNames: Get member names + // - getMembers: Access member references + // - memberIndices: Retrieve member indices + if (sdtypep->isConstrainedRand()) { + putns(sdtypep, "\nstd::vector memberNames(void) const {\n"); + puts("return {"); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) putns(itemp, "\"" + itemp->shortName() + "\""); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(",\n"); + } + puts("};\n}\n"); + putns(sdtypep, "\nauto memberIndices(void) const {\n"); + puts("return std::index_sequence_for<"); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) + putns(itemp, itemp->dtypep()->cType("", false, false)); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(",\n"); + } + puts(">{};\n}\n"); + + putns(sdtypep, "\ntemplate "); + putns(sdtypep, "\nauto getMembers(T& obj) {\n"); + puts("return std::tie("); + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (itemp->isConstrainedRand()) putns(itemp, "obj." + itemp->nameProtect()); + if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand()) + puts(", "); + } + puts(");\n}\n"); + } putns(sdtypep, "\nbool operator==(const " + EmitCBase::prefixNameProtect(sdtypep) + "& rhs) const {\n"); puts("return "); @@ -280,6 +316,9 @@ class EmitCHeader final : public EmitCConstInit { puts(");\n"); puts("}\n"); puts("};\n"); + puts("template <>\n"); + putns(sdtypep, "struct VlIsCustomStruct<" + EmitCBase::prefixNameProtect(sdtypep) + + "> : public std::true_type {};\n"); } // getfunc: VL_ASSIGNSEL_XX(rbits, obits, off, lhsdata, rhsdata); diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 6baa90919..d6b8b67ef 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -629,6 +629,11 @@ class ConstraintExprVisitor final : public VNVisitor { const uint32_t unpackedDimensions = dims.second; dimension = unpackedDimensions; } + if (VN_IS(varp->dtypeSkipRefp(), StructDType) + && !VN_AS(varp->dtypeSkipRefp(), StructDType)->packed()) { + VN_AS(varp->dtypeSkipRefp(), StructDType)->markConstrainedRand(true); + dimension = 1; + } methodp->dtypeSetVoid(); AstClass* const classp = VN_AS(varp->user2p(), Class); AstVarRef* const varRefp @@ -706,6 +711,26 @@ class ConstraintExprVisitor final : public VNVisitor { editSMT(nodep, nodep->fromp(), lsbp, msbp); } + void visit(AstStructSel* nodep) override { + if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) { + AstMemberDType* memberp + = VN_AS(nodep->fromp()->dtypep()->skipRefp(), StructDType)->membersp(); + while (memberp->nextp()) { + if (memberp->name() == nodep->name()) { + memberp->markConstrainedRand(true); + break; + } else + memberp = VN_CAST(memberp->nextp(), MemberDType); + } + } + iterateChildren(nodep); + if (editFormat(nodep)) return; + FileLine* const fl = nodep->fileline(); + AstSFormatF* const newp + = new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, nullptr}; + nodep->replaceWith(newp); + VL_DO_DANGLING(pushDeletep(nodep), nodep); + } void visit(AstAssocSel* nodep) override { if (editFormat(nodep)) return; FileLine* const fl = nodep->fileline(); diff --git a/test_regress/t/t_constraint_struct.py b/test_regress/t/t_constraint_struct.py new file mode 100755 index 000000000..a2b131082 --- /dev/null +++ b/test_regress/t/t_constraint_struct.py @@ -0,0 +1,21 @@ +#!/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('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_struct.v b/test_regress/t/t_constraint_struct.v new file mode 100755 index 000000000..4462017bb --- /dev/null +++ b/test_regress/t/t_constraint_struct.v @@ -0,0 +1,87 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct packed { + bit [7:0] byte_value; + int int_value; +} PackedStruct; + +typedef struct { + rand bit [7:0] byte_value; + rand int int_value; + int non_rand_value; // Non-randomized member +} UnpackedStruct; + +class PackedStructTest; + rand PackedStruct packed_struct; + + function new(); + packed_struct.byte_value = 8'hA0; + packed_struct.int_value = 0; + endfunction + + // Constraint block for packed struct + constraint packed_struct_constraint { + packed_struct.byte_value == 8'hA0; + packed_struct.int_value inside {[0:100]}; + } + + // Self-check function for packed struct + function void check(); + if (packed_struct.byte_value != 8'hA0) $stop; + if (!(packed_struct.int_value inside {[0:100]})) $stop; + endfunction +endclass + +class UnpackedStructTest; + rand UnpackedStruct unpacked_struct; + + function new(); + unpacked_struct.byte_value = 8'h00; + unpacked_struct.int_value = 0; + unpacked_struct.non_rand_value = 42; + endfunction + + // Constraint block for unpacked struct + constraint unpacked_struct_constraint { + unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0}; + unpacked_struct.int_value inside {[50:150]}; + } + + // Self-check function for unpacked struct + function void check(); + if (!(unpacked_struct.byte_value inside {8'hA0, 8'hB0, 8'hC0})) $stop; + if (!(unpacked_struct.int_value inside {[50:150]})) $stop; + if (unpacked_struct.non_rand_value != 42) $stop; // Check non-randomized member + endfunction +endclass + +module t_constraint_struct; + PackedStructTest packed_struct_test; + UnpackedStructTest unpacked_struct_test; + int success; + + initial begin + // Test packed struct + packed_struct_test = new(); + repeat(10) begin + success = packed_struct_test.randomize(); + if (success == 0) $stop; + packed_struct_test.check(); // Self-check for packed struct + end + + // Test unpacked struct + unpacked_struct_test = new(); + repeat(10) begin + success = unpacked_struct_test.randomize(); + if (success == 0) $stop; + unpacked_struct_test.check(); // Self-check for unpacked struct + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index fe4fb9b92..d0864dfe4 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -69,10 +69,10 @@ {"type":"BASICDTYPE","name":"logic","addr":"(RB)","loc":"d,24:7,24:12","dtypep":"(RB)","keyword":"logic","generic":false,"rangep": []}, {"type":"STRUCTDTYPE","name":"m.my_struct","addr":"(K)","loc":"d,20:12,20:18","dtypep":"(K)","packed":true,"isFourstate":true,"generic":false,"classOrPackagep":"UNLINKED", "membersp": [ - {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","generic":false,"childDTypep": [],"valuep": []}, - {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","generic":false,"childDTypep": [],"valuep": []} + {"type":"MEMBERDTYPE","name":"clk","addr":"(SB)","loc":"d,21:19,21:22","dtypep":"(OB)","isConstrainedRand":false,"name":"clk","tag":"this is clk","generic":false,"refDTypep":"(OB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"k","addr":"(TB)","loc":"d,22:19,22:20","dtypep":"(PB)","isConstrainedRand":false,"name":"k","tag":"","generic":false,"refDTypep":"(PB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"enable","addr":"(UB)","loc":"d,23:19,23:25","dtypep":"(QB)","isConstrainedRand":false,"name":"enable","tag":"enable","generic":false,"refDTypep":"(QB)","childDTypep": [],"valuep": []}, + {"type":"MEMBERDTYPE","name":"data","addr":"(VB)","loc":"d,24:19,24:23","dtypep":"(RB)","isConstrainedRand":false,"name":"data","tag":"data","generic":false,"refDTypep":"(RB)","childDTypep": [],"valuep": []} ]}, {"type":"IFACEREFDTYPE","name":"","addr":"(O)","loc":"d,29:8,29:12","dtypep":"(O)","isPortDecl":false,"isVirtual":false,"cellName":"itop","ifaceName":"ifc","modportName":"","generic":false,"ifacep":"UNLINKED","cellp":"(L)","modportp":"UNLINKED","paramsp": []}, {"type":"BASICDTYPE","name":"logic","addr":"(S)","loc":"d,31:27,31:28","dtypep":"(S)","keyword":"logic","range":"31:0","generic":true,"rangep": []}, From 5b65c15ecaa0073f3185c7dcf08a16e216bfd44f Mon Sep 17 00:00:00 2001 From: Moubarak Jeje <97465738+mjeje@users.noreply.github.com> Date: Wed, 5 Feb 2025 14:29:54 -0600 Subject: [PATCH 63/93] Internals: Add datap cast functions (#5761) --- docs/CONTRIBUTORS | 1 + include/verilated_vpi.cpp | 40 +++++++++++++++++++++++++++++++++------ 2 files changed, 35 insertions(+), 6 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index da6fa5589..ea9a0b110 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -155,6 +155,7 @@ Miodrag Milanović Mladen Slijepcevic Morten Borup Petersen Mostafa Gamal +Moubarak Jeje Nandu Raj Natan Kreimer Nathan Graybeal diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index 53f953564..be027161e 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -221,6 +221,34 @@ public: const char* name() const override { return m_varp->name(); } const char* fullname() const override { return m_fullname.c_str(); } virtual void* varDatap() const { return m_varp->datap(); } + CData* varCDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT8);); + return reinterpret_cast(varDatap()); + } + SData* varSDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT16);); + return reinterpret_cast(varDatap()); + } + IData* varIDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT32);); + return reinterpret_cast(varDatap()); + } + QData* varQDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_UINT64);); + return reinterpret_cast(varDatap()); + } + EData* varEDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_WDATA);); + return reinterpret_cast(varDatap()); + } + double* varRealDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_REAL);); + return reinterpret_cast(varDatap()); + } + std::string* varStringDatap() const { + VL_DEBUG_IFDEF(assert(varp()->vltype() == VLVT_STRING);); + return reinterpret_cast(varDatap()); + } virtual uint32_t bitOffset() const { return 0; } }; @@ -2584,7 +2612,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { } } else if (valuep->format == vpiBinStrVal) { t_outDynamicStr.resize(varBits); - const CData* datap = (reinterpret_cast(varDatap)); + const CData* datap = reinterpret_cast(varDatap); for (size_t i = 0; i < varBits; ++i) { const size_t pos = i + vop->bitOffset(); const char val = (datap[pos >> 3] >> (pos & 7)) & 1; @@ -2632,7 +2660,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { valuep->value.str = reinterpret_cast(varDatap); return; } else { - t_outDynamicStr = *(reinterpret_cast(varDatap)); + t_outDynamicStr = *(vop->varStringDatap()); valuep->value.str = const_cast(t_outDynamicStr.c_str()); return; } @@ -2651,7 +2679,7 @@ void vl_vpi_get_value(const VerilatedVpioVarBase* vop, p_vpi_value valuep) { valuep->value.integer = vl_vpi_get_word(vop, 32, 0); return; } else if (valuep->format == vpiRealVal) { - valuep->value.real = *(reinterpret_cast(varDatap)); + valuep->value.real = *(vop->varRealDatap()); return; } else if (valuep->format == vpiSuppressVal) { return; @@ -2739,7 +2767,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ } } else if (valuep->format == vpiBinStrVal) { const int len = std::strlen(valuep->value.str); - CData* const datap = (reinterpret_cast(vop->varDatap())); + CData* const datap = reinterpret_cast(vop->varDatap()); for (int i = 0; i < varBits; ++i) { const bool set = (i < len) && (valuep->value.str[len - i - 1] == '1'); const size_t pos = vop->bitOffset() + i; @@ -2819,7 +2847,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } else if (valuep->format == vpiStringVal) { if (vop->varp()->vltype() == VLVT_STRING) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.str; + *(vop->varStringDatap()) = valuep->value.str; return object; } else { const int chars = VL_BYTES_I(varBits); @@ -2836,7 +2864,7 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return object; } else if (valuep->format == vpiRealVal) { if (vop->varp()->vltype() == VLVT_REAL) { - *(reinterpret_cast(vop->varDatap())) = valuep->value.real; + *(vop->varRealDatap()) = valuep->value.real; return object; } } From 283f6c74332cb5fd41bd550abd0ea02958c5a380 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Fri, 7 Feb 2025 16:32:12 +0100 Subject: [PATCH 64/93] Add `--preproc-token-limit` (#5768) --- bin/verilator | 1 + docs/guide/exe_verilator.rst | 6 ++++++ src/V3Options.cpp | 4 ++++ src/V3Options.h | 2 ++ src/V3PreProc.cpp | 6 +++--- src/V3PreProc.h | 1 - test_regress/t/t_flag_values_bad.out | 1 + test_regress/t/t_flag_values_bad.py | 10 ++++++---- test_regress/t/t_pp_circ_subst_bad2.out | 3 +++ test_regress/t/t_pp_circ_subst_bad2.py | 19 +++++++++++++++++++ 10 files changed, 45 insertions(+), 8 deletions(-) create mode 100644 test_regress/t/t_pp_circ_subst_bad2.out create mode 100755 test_regress/t/t_pp_circ_subst_bad2.py diff --git a/bin/verilator b/bin/verilator index cf6512913..e2c3c343a 100755 --- a/bin/verilator +++ b/bin/verilator @@ -426,6 +426,7 @@ detailed descriptions of these arguments. --pipe-filter Filter all input through a script --pp-comments Show preprocessor comments with -E --prefix Name of top-level class + --preproc-token-limit Maximum tokens on a line allowed by preprocessor --private Debugging; see docs --prof-c Compile C++ code with profiling --prof-cfuncs Name functions for profiling diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index c73301b22..61a99b23e 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1142,6 +1142,12 @@ Summary: prepended to the name of the :vlopt:`--top` option, or V prepended to the first Verilog filename passed on the command line. +.. option:: --preproc-token-limit + + Rarely needed. Configure the limit of the number of tokens Verilator + can process on a single line to prevent infinite loops and other hangs. + Defaults to 40000 tokens. + .. option:: --private Opposite of :vlopt:`--public`. This is the default; this option exists for diff --git a/src/V3Options.cpp b/src/V3Options.cpp index bc9a7c9d5..1e2024d37 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1502,6 +1502,10 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, validateIdentifier(fl, valp, "--prefix"); m_prefix = valp; }); + DECL_OPTION("-preproc-token-limit", CbVal, [this, fl](const char* valp) { + m_preprocTokenLimit = std::atoi(valp); + if (m_preprocTokenLimit <= 0) fl->v3error("--preproc-token-limit must be > 0: " << valp); + }); DECL_OPTION("-private", CbCall, [this]() { m_public = false; }); DECL_OPTION("-prof-c", OnOff, &m_profC); DECL_OPTION("-prof-cfuncs", CbCall, [this]() { m_profC = m_profCFuncs = true; }); diff --git a/src/V3Options.h b/src/V3Options.h index 9606f7a42..9ec3918de 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -324,6 +324,7 @@ private: int m_outputSplitCFuncs = -1; // main switch: --output-split-cfuncs int m_outputSplitCTrace = -1; // main switch: --output-split-ctrace int m_pinsBv = 65; // main switch: --pins-bv + int m_preprocTokenLimit = 40000; // main switch: --preproc-token-limit int m_publicDepth = 0; // main switch: --public-depth int m_reloopLimit = 40; // main switch: --reloop-limit VOptionBool m_skipIdentical; // main switch: --skip-identical @@ -465,6 +466,7 @@ public: bool preprocOnly() const { return m_preprocOnly; } bool makePhony() const { return m_makePhony; } bool preprocNoLine() const { return m_preprocNoLine; } + int preprocTokenLimit() const { return m_preprocTokenLimit; } bool underlineZero() const { return m_underlineZero; } string flags() const { return m_flags; } bool systemC() const VL_MT_SAFE { return m_systemC; } diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index 203faf5bf..fa41f5582 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -963,9 +963,9 @@ int V3PreProcImp::getRawToken() { if (m_lastLineno != m_lexp->m_tokFilelinep->lineno()) { m_lastLineno = m_lexp->m_tokFilelinep->lineno(); m_tokensOnLine = 0; - } else if (++m_tokensOnLine > LINE_TOKEN_MAX) { - error("Too many preprocessor tokens on a line (>" + cvtToStr(LINE_TOKEN_MAX) - + "); perhaps recursive `define"); + } else if (++m_tokensOnLine > v3Global.opt.preprocTokenLimit()) { + error("Too many preprocessor tokens on a line (>" + + cvtToStr(v3Global.opt.preprocTokenLimit()) + "); perhaps recursive `define"); tok = VP_EOF_ERROR; } diff --git a/src/V3PreProc.h b/src/V3PreProc.h index da6c0d8d8..608447d4b 100644 --- a/src/V3PreProc.h +++ b/src/V3PreProc.h @@ -42,7 +42,6 @@ public: // CONSTANTS enum MiscConsts { DEFINE_RECURSION_LEVEL_MAX = 1000, // How many `def substitutions before an error - LINE_TOKEN_MAX = 40000, // How many tokens on a line before an error INCLUDE_DEPTH_MAX = 500, // How many `includes deep before an error // Streams deep (sometimes `def deep) before an error. // Set more than DEFINE_RECURSION_LEVEL_MAX or INCLUDE_DEPTH_MAX. diff --git a/test_regress/t/t_flag_values_bad.out b/test_regress/t/t_flag_values_bad.out index 8febf43ce..19b596488 100644 --- a/test_regress/t/t_flag_values_bad.out +++ b/test_regress/t/t_flag_values_bad.out @@ -1,4 +1,5 @@ %Error: --output-split-cfuncs must be >= 0: -1 %Error: --output-split-ctrace must be >= 0: -1 +%Error: --preproc-token-limit must be > 0: 0 %Error: --reloop-limit must be >= 2: -1 %Error: Exiting due to diff --git a/test_regress/t/t_flag_values_bad.py b/test_regress/t/t_flag_values_bad.py index ab8c8bb79..0bebd8379 100755 --- a/test_regress/t/t_flag_values_bad.py +++ b/test_regress/t/t_flag_values_bad.py @@ -11,9 +11,11 @@ import vltest_bootstrap test.scenarios('vlt') -test.lint( - verilator_flags2=["--output-split-cfuncs -1", "--output-split-ctrace -1", "--reloop-limit -1"], - fails=True, - expect_filename=test.golden_filename) +test.lint(verilator_flags2=[ + "--output-split-cfuncs -1", "--output-split-ctrace -1", "--preproc-token-limit 0", + "--reloop-limit -1" +], + fails=True, + expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_pp_circ_subst_bad2.out b/test_regress/t/t_pp_circ_subst_bad2.out new file mode 100644 index 000000000..657d44e8a --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.out @@ -0,0 +1,3 @@ +%Error: t/t_pp_circ_subst_bad.v:8:40002: Too many preprocessor tokens on a line (>20000); perhaps recursive `define +%Error: t/t_pp_circ_subst_bad.v:8:1: syntax error, unexpected IDENTIFIER +%Error: Exiting due to diff --git a/test_regress/t/t_pp_circ_subst_bad2.py b/test_regress/t/t_pp_circ_subst_bad2.py new file mode 100755 index 000000000..a4770180d --- /dev/null +++ b/test_regress/t/t_pp_circ_subst_bad2.py @@ -0,0 +1,19 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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.top_filename = "t/t_pp_circ_subst_bad.v" + +test.lint(verilator_flags2=["--preproc-token-limit 20000"], + fails=True, + expect_filename=test.golden_filename) + +test.passes() From ac659d525b56a9573cec7d4a470f05123fc41b01 Mon Sep 17 00:00:00 2001 From: Moubarak Jeje <97465738+mjeje@users.noreply.github.com> Date: Fri, 7 Feb 2025 18:06:46 -0600 Subject: [PATCH 65/93] Support VPI array accessors (#5612) --- docs/CONTRIBUTORS | 2 + include/verilated_vpi.cpp | 636 ++++++++++++++++++- include/verilatedos.h | 1 + test_regress/t/TestVpi.h | 1 + test_regress/t/t_vpi_get_value_array.cpp | 695 +++++++++++++++++++++ test_regress/t/t_vpi_get_value_array.py | 23 + test_regress/t/t_vpi_get_value_array.v | 105 ++++ test_regress/t/t_vpi_put_value_array.cpp | 759 +++++++++++++++++++++++ test_regress/t/t_vpi_put_value_array.py | 23 + test_regress/t/t_vpi_put_value_array.v | 76 +++ test_regress/t/t_vpi_unimpl.cpp | 2 - 11 files changed, 2315 insertions(+), 8 deletions(-) create mode 100644 test_regress/t/t_vpi_get_value_array.cpp create mode 100755 test_regress/t/t_vpi_get_value_array.py create mode 100644 test_regress/t/t_vpi_get_value_array.v create mode 100644 test_regress/t/t_vpi_put_value_array.cpp create mode 100755 test_regress/t/t_vpi_put_value_array.py create mode 100644 test_regress/t/t_vpi_put_value_array.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index ea9a0b110..9fd2ac91e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -41,6 +41,7 @@ David Metz David Stanford David Turner Dercury +Diego Roux Don Williamson Drew Ranck Drew Taussig @@ -99,6 +100,7 @@ Jesse Taube Jevin Sweval Jiacheng Qian Jiamin Zhu +Jitesh Nayak Jinyan Xu Jiuyang Liu Joey Liu diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index be027161e..ac885332d 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -2884,13 +2884,637 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ return nullptr; } -void vpi_get_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); +bool vl_check_array_format(const VerilatedVar* varp, const p_vpi_arrayvalue arrayvalue_p, + const char* fullname) { + if (arrayvalue_p->format == vpiVectorVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: return true; + default:; + } + } else if ((arrayvalue_p->format == vpiRawTwoStateVal) + || (arrayvalue_p->format == vpiRawFourStateVal)) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: + case VLVT_WDATA: return true; + default:; + } + } else if (arrayvalue_p->format == vpiShortIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: return true; + default:; + } + } else if (arrayvalue_p->format == vpiLongIntVal) { + switch (varp->vltype()) { + case VLVT_UINT8: + case VLVT_UINT16: + case VLVT_UINT32: + case VLVT_UINT64: return true; + default:; + } + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), fullname); + + return false; } -void vpi_put_value_array(vpiHandle /*object*/, p_vpi_arrayvalue /*arrayvalue_p*/, - PLI_INT32* /*index_p*/, PLI_UINT32 /*num*/) { - VL_VPI_UNIMP_(); + +template +void vl_get_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(sizeof(K) >= sizeof(T), "size of type K is less than size of type T"); + for (int i = 0; i < num; i++) { + dst[i] = src[index]; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_integrals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + K* dst) { + static_assert(std::is_integral::value, "type T is not an integral type"); + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(sizeof(T) >= sizeof(K), "size of type T is less than size of type K"); + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const T mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i] & static_cast(mask); + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_get_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, const T* src, + p_vpi_vecval dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); // ensure logical right shift + const unsigned element_size_bytes = VL_BYTES_I(packedSize); + const unsigned element_size_words = VL_WORDS_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + if (sizeof(T) == sizeof(QData)) { + for (unsigned i = 0; i < num; i++) { + dst[i * 2].aval = static_cast(src[index]); + dst[i * 2].bval = 0; + dst[(i * 2) + 1].aval = static_cast(src[index]) >> 32; + dst[(i * 2) + 1].bval = 0; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + const size_t dst_index = i * element_size_words; + const size_t src_index = index * element_size_words; + for (unsigned j = 0; j < element_size_words; j++) { + dst[dst_index + j].aval = src[src_index + j]; + dst[dst_index + j].bval = 0; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_put_value_array_vectors(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const p_vpi_vecval src, T* dst) { + static_assert(std::is_unsigned::value, "type T is not unsigned"); + static_assert(std::is_integral::value, "type T is not an integral type"); + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_words VL_WORDS_I(packedSize); + if (sizeof(T) == sizeof(QData)) { //destination is QDATA + const QData mask = element_size_bytes == sizeof(T) + ? static_cast(-1) + : ~(static_cast(-1) << (element_size_bytes * 8)); + for (unsigned i = 0; i < num; i++) { + dst[index] = src[i * 2].aval; + dst[index] + |= (static_cast(src[(i * 2) + 1].aval) << (sizeof(PLI_UINT32) * 8)) & mask; + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } else { + for (unsigned i = 0; i < num; i++) { + unsigned bytes_stored = 0; + for (unsigned j = 0; j < element_size_words; j++) { + if (bytes_stored >= element_size_bytes) break; + const T mask + = (element_size_bytes - bytes_stored) >= sizeof(PLI_UINT32) + ? static_cast(-1) + : ~(static_cast(-1) << ((element_size_bytes - bytes_stored) * 8)); + dst[(index * element_size_words) + j] + = static_cast(src[(i * element_size_words) + j].aval) & mask; + bytes_stored += sizeof(PLI_UINT32); + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } + } +} + +template +void vl_get_value_array_rawvals(unsigned index, unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const T* src, PLI_BYTE8* dst) { + static_assert(std::is_unsigned::value, + "type T is not unsigned"); //ensure loigcal right shift + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + size_t dst_index = 0; + while (num-- > 0) { + const size_t src_offset = index * element_size_repr; + unsigned bytes_copied = 0; + for (unsigned j = 0; j < element_size_repr; j++) { + const T& src_data = src[src_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied++ == element_size_bytes) break; + dst[dst_index++] = src_data >> (k * 8); + } + } + if (fourState) { + std::fill(dst + dst_index, dst + dst_index + element_size_bytes, 0); + dst_index += element_size_bytes; + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +template +void vl_put_value_array_rawvals(unsigned index, const unsigned num, const unsigned size, + const unsigned packedSize, const bool leftIsLow, + const bool fourState, const PLI_UBYTE8* src, T* dst) { + const unsigned element_size_bytes VL_BYTES_I(packedSize); + const unsigned element_size_repr = (element_size_bytes + sizeof(T) - 1) / sizeof(T); + for (unsigned i = 0; i < num; i++) { + unsigned bytes_copied = 0; + const size_t dst_offset = index * element_size_repr; + const size_t src_offset = i * element_size_bytes; + for (unsigned j = 0; j < element_size_repr; j++) { + T& dst_data = dst[dst_offset + j]; + for (unsigned k = 0; k < sizeof(T); k++) { + if (bytes_copied == element_size_bytes) break; + const unsigned src_index + = fourState ? (src_offset * 2) + bytes_copied : (src_offset) + bytes_copied; + dst_data &= ~((static_cast(0xFF) & 0xFF) << (k * 8)); + dst_data |= ((static_cast(src[src_index]) & 0xFF) << (k * 8)); + bytes_copied++; + } + } + index = leftIsLow ? index == (size - 1) ? 0 : index + 1 + : index == 0 ? size - 1 + : index - 1; + } +} + +void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + static thread_local EData out_data[VL_VALUE_STRING_MAX_WORDS * 2]; + + const unsigned size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: requested elements (%u) exceed array size (%u)", + __func__, num, size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT16) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT16* shortintsp = (PLI_INT16*)out_data; + arrayvalue_p->value.shortints = shortintsp; + + if (varp->vltype() == VLVT_UINT8) { + const CData* ptr = reinterpret_cast(vop->varDatap()); + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), shortintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), shortintsp); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + if (VL_UNCOVERABLE(num >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT32* integersp = (PLI_INT32*)out_data; + arrayvalue_p->value.integers = integersp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), integersp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), integersp); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + if (VL_UNCOVERABLE((sizeof(PLI_INT64) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_INT64* longintsp = (PLI_INT64*)out_data; + arrayvalue_p->value.longints = longintsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), longintsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), longintsp); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + if (VL_UNCOVERABLE((VL_WORDS_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_WORDS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + p_vpi_vecval vectorsp = (p_vpi_vecval)out_data; + arrayvalue_p->value.vectors = vectorsp; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varCDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varSDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varIDatap(), vectorsp); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varQDatap(), vectorsp); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, + vop->varEDatap(), vectorsp); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * 2 * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, + vop->varEDatap(), valuep); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + if (VL_UNCOVERABLE((VL_BYTES_I(varp->entBits()) * num) >= VL_VALUE_STRING_MAX_CHARS)) { + VL_FATAL_MT(__FILE__, __LINE__, "", + "vpi_get_value_array with more than VL_VALUE_STRING_MAX_WORDS; " + "increase and recompile"); + } + + PLI_BYTE8* valuep = (PLI_BYTE8*)out_data; + arrayvalue_p->value.rawvals = valuep; + + if (varp->vltype() == VLVT_UINT8) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varCDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT16) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varSDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT32) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varIDatap(), valuep); + } else if (varp->vltype() == VLVT_UINT64) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varQDatap(), valuep); + } else if (varp->vltype() == VLVT_WDATA) { + vl_get_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, + vop->varEDatap(), valuep); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_get_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + + VL_VPI_ERROR_RESET_(); + if (VL_UNLIKELY(!object)) return; + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_get_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + const VerilatedVar* const varp = vop->varp(); + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (arrayvalue_p->flags & vpiUserAllocFlag) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiUserAllocFlag (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_get_value_array(object, arrayvalue_p, index_p, num); +} + +void vl_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (!vl_check_array_format(vop->varp(), arrayvalue_p, vop->fullname())) return; + + const VerilatedVar* const varp = vop->varp(); + + int size = vop->size(); + if (VL_UNCOVERABLE(num > size)) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "%s: requested elements to set (%u) exceed array size (%u)", __func__, num, + size); + return; + } + + const bool leftIsLow = vop->rangep()->left() == vop->rangep()->low(); + int index + = leftIsLow ? index_p[0] - vop->rangep()->left() : vop->rangep()->left() - index_p[0]; + + if (arrayvalue_p->format == vpiShortIntVal) { + const PLI_UINT16* shortintsp + = reinterpret_cast(arrayvalue_p->value.shortints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, shortintsp, + vop->varSDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiIntVal) { + const PLI_UINT32* integersp = reinterpret_cast(arrayvalue_p->value.integers); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, integersp, + vop->varIDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiLongIntVal) { + const PLI_UINT64* longintsp = reinterpret_cast(arrayvalue_p->value.longints); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, longintsp, + vop->varQDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiVectorVal) { + const p_vpi_vecval vectorsp = arrayvalue_p->value.vectors; + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_vectors(index, num, size, varp->entBits(), leftIsLow, true, + vectorsp, vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawFourStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, true, valuep, + vop->varEDatap()); + } + + return; + } else if (arrayvalue_p->format == vpiRawTwoStateVal) { + const PLI_UBYTE8* valuep = reinterpret_cast(arrayvalue_p->value.rawvals); + + if (varp->vltype() == VLVT_UINT8) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varCDatap()); + } else if (varp->vltype() == VLVT_UINT16) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varSDatap()); + } else if (varp->vltype() == VLVT_UINT32) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varIDatap()); + } else if (varp->vltype() == VLVT_UINT64) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varQDatap()); + } else if (varp->vltype() == VLVT_WDATA) { + vl_put_value_array_rawvals(index, num, size, varp->entBits(), leftIsLow, false, valuep, + vop->varEDatap()); + } + + return; + } + + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported format (%s) as requested for %s", __func__, + VerilatedVpiError::strFromVpiVal(arrayvalue_p->format), vop->fullname()); +} + +void vpi_put_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT32* index_p, + PLI_UINT32 num) { + VL_DEBUG_IF_PLI(VL_DBG_MSGF("- vpi: vpi_put_value_array %p\n", object);); + VerilatedVpiImp::assertOneCheck(); + VL_VPI_ERROR_RESET_(); + + if (VL_UNLIKELY(!arrayvalue_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null value pointer"); + return; + } + + if (VL_UNLIKELY(!index_p)) { + VL_VPI_WARNING_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array with null index pointer"); + return; + } + + const VerilatedVpioVar* const vop = VerilatedVpioVar::castp(object); + if (VL_UNLIKELY(!vop)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported vpiHandle (%p)", __func__, object); + return; + } + + if (vop->type() != vpiRegArray) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported type (%p, %s)", __func__, object, + VerilatedVpiError::strFromVpiObjType(vop->type())); + return; + } + + const VerilatedVar* const varp = vop->varp(); + + int lowRange = vop->rangep()->low(); + int highRange = vop->rangep()->high(); + + if ((index_p[0] > highRange) || (index_p[0] < lowRange)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: index %u for object %s is out of bounds [%u,%u]", + __func__, index_p[0], vop->fullname(), lowRange, highRange); + return; + } + + if (VL_UNLIKELY(!vop->varp()->isPublicRW())) { + VL_VPI_ERROR_(__FILE__, __LINE__, + "Ignoring vpi_put_value_array to signal marked read-only," + " use public_flat_rw instead: %s", + vop->fullname()); + return; + } + + if (arrayvalue_p->flags & (vpiPropagateOff | vpiOneValue)) { + VL_VPI_ERROR_(__FILE__, __LINE__, "%s: Unsupported flags (%x)", __func__, + arrayvalue_p->flags); + return; + } + + vl_put_value_array(object, arrayvalue_p, index_p, num); } // time processing diff --git a/include/verilatedos.h b/include/verilatedos.h index 4022ac2b0..be0cd59a2 100644 --- a/include/verilatedos.h +++ b/include/verilatedos.h @@ -474,6 +474,7 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read() #ifndef VL_VALUE_STRING_MAX_WORDS #define VL_VALUE_STRING_MAX_WORDS 64 ///< Max size in words of String conversion operation #endif +#define VL_VALUE_STRING_MAX_CHARS (VL_VALUE_STRING_MAX_WORDS) * 4 //========================================================================= // Base macros diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 4a87b8e51..4c3bbc651 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -11,6 +11,7 @@ #include "sv_vpi_user.h" #include "vpi_user.h" +#include // Avoid C++11 in this file as not all simulators allow it diff --git a/test_regress/t/t_vpi_get_value_array.cpp b/test_regress/t/t_vpi_get_value_array.cpp new file mode 100644 index 000000000..86796eb04 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.cpp @@ -0,0 +1,695 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. 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 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_get_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw two state + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (offset * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(offset * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test vector + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", (i * elem_size_words) + j, + (offset * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(offset * elem_size_words) + j].aval); + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[offset]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[offset]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned low, + const unsigned num, const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u low=%u num=%u size=%u elem_size=%u\n\n", __func__, name, + index, low, num, size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + index -= low; + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; +#ifdef TEST_VERBOSE + printf("array[%u] == test[%u]\n", i, offset); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[offset]); + } + + return 0; +} + +int mon_check_props() { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 read_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 read_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 read_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 read_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 read_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char read_bytes_name[] = "test.read_bytes"; + char read_bytes_nonzero_index_name[] = "test.read_bytes_nonzero_index"; + char read_bytes_rl_name[] = "test.read_bytes_rl"; + char read_shorts_name[] = "test.read_shorts"; + char read_words_name[] = "test.read_words"; + char read_integers_name[] = "test.read_integers"; + char read_longs_name[] = "test.read_longs"; + char read_customs_name[] = "test.read_customs"; + char read_customs_nonzero_index_rl_name[] = "test.read_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(read_customs_name, read_customs, i, 0, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(read_customs_nonzero_index_rl_name, read_customs, i + 1, 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(read_bytes_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_nonzero_index_name, read_bytes, i + 1, 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_bytes_rl_name, read_bytes, i, 0, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(read_shorts_name, read_shorts, i, 0, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(read_words_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_integers_name, read_words, i, 0, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(read_longs_name, read_longs, i, 0, j, NUM_ELEMENTS, 8)) + return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported foramt + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortRealVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_longs", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiTimeVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_scalar", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test indexp out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_bounds", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {4}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = vpiUserAllocFlag; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & vltype combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, NUM_ELEMENTS); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.read_words", NULL); + CHECK_RESULT_NZ(object); + + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = 0; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_get_value_array.py b/test_regress/t/t_vpi_get_value_array.py new file mode 100755 index 000000000..9f7ed1832 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.py @@ -0,0 +1,23 @@ +#!/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('simulator') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_get_value_array.v b/test_regress/t/t_vpi_get_value_array.v new file mode 100644 index 000000000..86a82df92 --- /dev/null +++ b/test_regress/t/t_vpi_get_value_array.v @@ -0,0 +1,105 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. 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 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] read_bytes [0:3] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RD; + reg [7:0] read_bytes_rl [3:0] `PUBLIC_FLAT_RD; + + reg [15:0] read_shorts [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words [0:3] `PUBLIC_FLAT_RD; + reg [31:0] read_words_rl [3:0] `PUBLIC_FLAT_RD; + reg [63:0] read_longs [0:3] `PUBLIC_FLAT_RD; + integer read_integers [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs [0:3] `PUBLIC_FLAT_RD; + reg [68:0] read_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RD; + + reg [7:0] read_scalar `PUBLIC_FLAT_RD; + reg [7:0] read_bounds [1:3] `PUBLIC_FLAT_RD; + + integer status; + + initial begin + read_bytes[0] = 8'had; + read_bytes[1] = 8'hde; + read_bytes[2] = 8'hef; + read_bytes[3] = 8'hbe; + + read_bytes_rl[3] = 8'had; + read_bytes_rl[2] = 8'hde; + read_bytes_rl[1] = 8'hef; + read_bytes_rl[0] = 8'hbe; + + read_bytes_nonzero_index[1] = 8'had; + read_bytes_nonzero_index[2] = 8'hde; + read_bytes_nonzero_index[3] = 8'hef; + read_bytes_nonzero_index[4] = 8'hbe; + + read_shorts[0] = 16'hdead; + read_shorts[1] = 16'hbeef; + read_shorts[2] = 16'hcafe; + read_shorts[3] = 16'hf00d; + + read_words[0] = 32'hdeadbeef; + read_words[1] = 32'hcafef00d; + read_words[2] = 32'h00010203; + read_words[3] = 32'h04050607; + + read_integers[0] = 32'hdeadbeef; + read_integers[1] = 32'hcafef00d; + read_integers[2] = 32'h00010203; + read_integers[3] = 32'h04050607; + + read_longs[0] = 64'hdeadbeefcafef00d; + read_longs[1] = 64'h0001020304050607; + read_longs[2] = 64'h08090a0b0c0d0e0f; + read_longs[3] = 64'h1011121314151617; + + read_customs[0] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs[1] = 69'hF50001020304050607; + read_customs[2] = 69'h0A08090a0b0c0d0e0f; + read_customs[3] = 69'h051011121314151617; + + read_customs_nonzero_index_rl[4] = 69'hFAdeadbeefcafef00d; //0x001F'FFFF'FFFF'FFFF'FFFF + read_customs_nonzero_index_rl[3] = 69'hF50001020304050607; + read_customs_nonzero_index_rl[2] = 69'h0A08090a0b0c0d0e0f; + read_customs_nonzero_index_rl[1] = 69'h051011121314151617; + + +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_get_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_put_value_array.cpp b/test_regress/t/t_vpi_put_value_array.cpp new file mode 100644 index 000000000..2e9008ba4 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.cpp @@ -0,0 +1,759 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// +// Copyright 2024 by Diego Roux. 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 +// +//************************************************************************* + +#ifndef IS_VPI + +#include "verilated.h" +#include "verilated_vpi.h" + +#include "Vt_vpi_put_value_array.h" + +#endif + +// These require the above. Comment prevents clang-format moving them +#include "TestSimulator.h" +#include "TestVpi.h" + +#include + +//====================================================================== + +int test_vpiRawFourStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index and test data arrays + int index_arr[1] = {index}; + std::vector test_data_four_state; + test_data_four_state.reserve(size * elem_size * 2); + for (unsigned i = 0; i < size; i++) { + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(i * 2 * elem_size) + j] = test_data[(i * elem_size) + j]; + } + for (unsigned j = 0; j < elem_size; j++) { + test_data_four_state[(((i * 2) + 1) * elem_size) + j] = -1; // bval should be ignored + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawFourStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data_four_state.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to nu + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (2 * size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data_four_state[i] & 0xFF); + } +#endif + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * 2 * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * 2 * elem_size) + j], + test_data[(i * elem_size) + j]); + } + for (unsigned j = 0; j < elem_size; j++) { + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(((i * 2) + 1) * elem_size) + j], 0); + } + } + + return 0; +} + +int test_vpiRawTwoStateVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRawTwoStateVal; + arrayvalue.flags = 0; + arrayvalue.value.rawvals = test_data; + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.rawvals = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < (size * elem_size); i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.rawvals[i] & 0xFF, i, + test_data[i] & 0xFF); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size) + j, (i * elem_size) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.rawvals[(i * elem_size) + j], + test_data[(i * elem_size) + j]); + } + } + + return 0; +} + +int test_vpiVectorVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + // prepare index + int index_arr[1] = {index}; + const unsigned elem_size_words = (elem_size + 3) / sizeof(PLI_UINT32); + const unsigned vec_size = elem_size_words * size; + std::vector test_data_vectors; + test_data_vectors.reserve(vec_size); + unsigned test_data_index = 0; + for (unsigned i = 0; i < size; i++) { + unsigned count = 0; + for (unsigned j = 0; j < elem_size_words; j++) { + PLI_UINT32& aval = test_data_vectors[(i * elem_size_words) + j].aval; + test_data_vectors[(i * elem_size_words) + j].bval = UINT32_MAX; + aval = 0; + for (unsigned k = 0; k < sizeof(PLI_UINT32); k++) { + if (count++ == elem_size) break; + aval |= static_cast(test_data[test_data_index++] & 0xFF) << (k * 8); + } + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiVectorVal; + arrayvalue.flags = 0; + arrayvalue.value.vectors = test_data_vectors.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < vec_size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.vectors[i].aval, i, + test_data_vectors[i].aval); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { + const unsigned offset = (index + i) % size; + for (unsigned j = 0; j < elem_size_words; j++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", (i * elem_size_words) + j, (i * elem_size_words) + j); +#endif + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].aval, + test_data_vectors[(i * elem_size_words) + j].aval); + } + for (unsigned j = 0; j < elem_size_words; j++) { + CHECK_RESULT_HEX(arrayvalue.value.vectors[(i * elem_size_words) + j].bval, 0); + } + } + + return 0; +} + +int test_vpiIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_integers; + test_data_integers.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT32& integer = test_data_integers[i]; + integer = 0; + for (unsigned j = 0; j < elem_size; j++) { + integer |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = test_data_integers.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.integers[i], i, + test_data_integers[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.integers[i], test_data_integers[i]); + } + + return 0; +} + +int test_vpiShortIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_shortints; + test_data_shortints.reserve(size); + for (unsigned i = 0; i < size; i++) { + if (elem_size == 2) { + test_data_shortints[i] = test_data[i * 2] & 0xFF; + test_data_shortints[i] |= test_data[(i * 2) + 1] << 8; + } else { + test_data_shortints[i] = test_data[i] & 0xFF; + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.shortints = test_data_shortints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + +#ifdef TEST_VERBOSE + for (unsigned i = 0; i < size; i++) { + printf("arr[%u]=%x test[%u]=%x\n", i, arrayvalue.value.shortints[i], i, + test_data_shortints[i]); + } +#endif + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.shortints[i], test_data_shortints[i]); + } + + return 0; +} + +int test_vpiLongIntVal(char* name, PLI_BYTE8* test_data, int index, const unsigned num, + const unsigned size, const unsigned elem_size) { +#ifdef TEST_VERBOSE + printf("%%\n%s: name=%s index=%u num=%u size=%u elem_size=%u\n\n", __func__, name, index, num, + size, elem_size); +#endif + + // prepare index + int index_arr[1] = {index}; + std::vector test_data_longints; + test_data_longints.reserve(size); + for (unsigned i = 0; i < size; i++) { + PLI_INT64& longint = test_data_longints[i]; + longint = 0; + for (unsigned j = 0; j < elem_size; j++) { + longint |= (static_cast(test_data[(i * elem_size) + j]) & 0xFF) << (j * 8); + } + } + + // get array handle + TestVpiHandle arrayhandle = vpi_handle_by_name(name, NULL); + CHECK_RESULT_NZ(arrayhandle); + + // test raw fourstate + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiLongIntVal; + arrayvalue.flags = 0; + arrayvalue.value.longints = test_data_longints.data(); + vpi_put_value_array(arrayhandle, &arrayvalue, index_arr, num); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // get value to check + arrayvalue.value.vectors = 0; + vpi_get_value_array(arrayhandle, &arrayvalue, index_arr, size); + CHECK_RESULT_NZ(!vpi_chk_error(0)); + + // compare to test data + for (unsigned i = 0; i < num; i++) { +#ifdef TEST_VERBOSE + printf("arr[%u] == test[%u]\n", i, i); +#endif + CHECK_RESULT_HEX(arrayvalue.value.longints[i], test_data_longints[i]); + } + + return 0; +} + +int mon_check_props(void) { + // skip test if not verilator (value_array accessors unimplemented in other sims) + if (!TestSimulator::is_verilator()) { +#ifdef VERILATOR + printf("TestSimulator indicating not verilator, but VERILATOR macro is defined\n"); + return 1; +#endif + return 0; + } + + const unsigned NUM_ELEMENTS = 4; + + PLI_BYTE8 write_bytes[NUM_ELEMENTS] + = {static_cast(0xad), static_cast(0xde), + static_cast(0xef), static_cast(0xbe)}; + + PLI_BYTE8 write_shorts[NUM_ELEMENTS * 2] = { + static_cast(0xad), static_cast(0xde), static_cast(0xef), + static_cast(0xbe), static_cast(0xfe), static_cast(0xca), + static_cast(0x0d), static_cast(0xf0)}; + + PLI_BYTE8 write_words[NUM_ELEMENTS * 4] = { + static_cast(0xef), static_cast(0xbe), static_cast(0xad), + static_cast(0xde), static_cast(0x0d), static_cast(0xf0), + static_cast(0xfe), static_cast(0xca), static_cast(0x03), + static_cast(0x02), static_cast(0x01), static_cast(0x00), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04)}; + + PLI_BYTE8 write_longs[NUM_ELEMENTS * 8] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x07), + static_cast(0x06), static_cast(0x05), static_cast(0x04), + static_cast(0x03), static_cast(0x02), static_cast(0x01), + static_cast(0x00), static_cast(0x0F), static_cast(0x0E), + static_cast(0x0D), static_cast(0x0C), static_cast(0x0B), + static_cast(0x0A), static_cast(0x09), static_cast(0x08), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10)}; + + PLI_BYTE8 write_customs[NUM_ELEMENTS * 9] = { + static_cast(0x0d), static_cast(0xf0), static_cast(0xfe), + static_cast(0xca), static_cast(0xef), static_cast(0xbe), + static_cast(0xad), static_cast(0xde), static_cast(0x1A), + static_cast(0x07), static_cast(0x06), static_cast(0x05), + static_cast(0x04), static_cast(0x03), static_cast(0x02), + static_cast(0x01), static_cast(0x00), static_cast(0x15), + static_cast(0x0F), static_cast(0x0E), static_cast(0x0D), + static_cast(0x0C), static_cast(0x0B), static_cast(0x0A), + static_cast(0x09), static_cast(0x08), static_cast(0x0A), + static_cast(0x17), static_cast(0x16), static_cast(0x15), + static_cast(0x14), static_cast(0x13), static_cast(0x12), + static_cast(0x11), static_cast(0x10), static_cast(0x05)}; + + char write_bytes_name[] = "test.write_bytes"; + char write_bytes_nonzero_index_name[] = "test.write_bytes_nonzero_index"; + char write_bytes_rl_name[] = "test.write_bytes_rl"; + char write_shorts_name[] = "test.write_shorts"; + char write_words_name[] = "test.write_words"; + char write_integers_name[] = "test.write_integers"; + char write_longs_name[] = "test.write_longs"; + char write_customs_name[] = "test.write_customs"; + char write_customs_nonzero_index_rl_name[] = "test.write_customs_nonzero_index_rl"; + + for (unsigned i = 0; i < NUM_ELEMENTS; i++) { + for (unsigned j = 0; j < (NUM_ELEMENTS + 1); j++) { + if (test_vpiRawFourStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawFourStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawFourStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawFourStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawFourStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawFourStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiRawTwoStateVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiRawTwoStateVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiRawTwoStateVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiRawTwoStateVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiRawTwoStateVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, + j, NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiVectorVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiVectorVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiVectorVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiVectorVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiVectorVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiVectorVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + if (test_vpiVectorVal(write_customs_name, write_customs, i, j, NUM_ELEMENTS, 9)) + return 1; + if (test_vpiVectorVal(write_customs_nonzero_index_rl_name, write_customs, i + 1, j, + NUM_ELEMENTS, 9)) + return 1; + + if (test_vpiShortIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiShortIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + + if (test_vpiIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, NUM_ELEMENTS, + 1)) + return 1; + if (test_vpiIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + + if (test_vpiLongIntVal(write_bytes_name, write_bytes, i, j, NUM_ELEMENTS, 1)) return 1; + if (test_vpiLongIntVal(write_bytes_nonzero_index_name, write_bytes, i + 1, j, + NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_bytes_rl_name, write_bytes, i, j, NUM_ELEMENTS, 1)) + return 1; + if (test_vpiLongIntVal(write_shorts_name, write_shorts, i, j, NUM_ELEMENTS, 2)) + return 1; + if (test_vpiLongIntVal(write_words_name, write_words, i, j, NUM_ELEMENTS, 4)) return 1; + if (test_vpiLongIntVal(write_integers_name, write_words, i, j, NUM_ELEMENTS, 4)) + return 1; + if (test_vpiLongIntVal(write_longs_name, write_longs, i, j, NUM_ELEMENTS, 8)) return 1; + } + } + + { + // test unsupported format + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_longs", NULL); + CHECK_RESULT_NZ(object); + + double datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiRealVal; + arrayvalue.flags = 0; + arrayvalue.value.reals = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiShortRealVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.format = vpiTimeVal; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null array value + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, 0, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported TestVpiHandle + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported type + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_scalar", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test index out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_bounds", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {4}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + indexp[0] = 0; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test inaccessible + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_inaccessible", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported flags + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiPropagateOff; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test unsupported format & type combination + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiShortIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + + arrayvalue.flags = vpiOneValue; + vpi_put_value_array(object, &arrayvalue, indexp, 4); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test num out of bounds + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + PLI_INT32 indexp[1] = {0}; + + vpi_put_value_array(object, &arrayvalue, indexp, 5); + CHECK_RESULT_NZ(~vpi_chk_error(0)); + } + + { + // test null arrayvalue + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + PLI_INT32 indexp[1] = {0}; + + vpi_get_value_array(object, 0, indexp, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + { + // test null indexp + TestVpiHandle object = vpi_handle_by_name((PLI_BYTE8*)"test.write_words", NULL); + CHECK_RESULT_NZ(object); + + int datap[4] = {0, 0, 0, 0}; + s_vpi_arrayvalue arrayvalue; + arrayvalue.format = vpiIntVal; + arrayvalue.flags = 0; + arrayvalue.value.integers = datap; + + vpi_get_value_array(object, &arrayvalue, 0, 0); + CHECK_RESULT_NZ(vpi_chk_error(0)); + } + + return 0; +} + +extern "C" int mon_check(void) { return mon_check_props(); } + +#ifdef IS_VPI + +static int mon_check_vpi() { + TestVpiHandle href = vpi_handle(vpiSysTfCall, 0); + s_vpi_value vpi_value; + + vpi_value.format = vpiIntVal; + vpi_value.value.integer = mon_check(); + vpi_put_value(href, &vpi_value, NULL, vpiNoDelay); + + return 0; +} + +static s_vpi_systf_data vpi_systf_data[] = {{vpiSysFunc, vpiIntFunc, (PLI_BYTE8*)"$mon_check", + (PLI_INT32(*)(PLI_BYTE8*))mon_check_vpi, 0, 0, 0}, + 0}; + +// cver entry +void vpi_compat_bootstrap(void) { + p_vpi_systf_data systf_data_p; + systf_data_p = &(vpi_systf_data[0]); + while (systf_data_p->type != 0) vpi_register_systf(systf_data_p++); +} + +// icarus entry +void (*vlog_startup_routines[])() = {vpi_compat_bootstrap, 0}; + +#else + +int main(int argc, char** argv) { + Verilated::commandArgs(argc, argv); + const std::unique_ptr contextp{new VerilatedContext}; + const std::unique_ptr top{new VM_PREFIX{contextp.get(), ""}}; + contextp->fatalOnVpiError(0); + +#ifdef VERILATOR +#ifdef TEST_VERBOSE + contextp->scopesDump(); +#endif +#endif + + while (!contextp->gotFinish()) { top->eval(); } + + return 0; +} +#endif diff --git a/test_regress/t/t_vpi_put_value_array.py b/test_regress/t/t_vpi_put_value_array.py new file mode 100755 index 000000000..9f7ed1832 --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.py @@ -0,0 +1,23 @@ +#!/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('simulator') + +test.compile(make_top_shell=False, + make_main=False, + make_pli=True, + verilator_flags2=["--exe --vpi --no-l2name", test.pli_filename], + iv_flags2=["-g2005-sv -D USE_VPI_NOT_DPI"], + v_flags2=["+define+USE_VPI_NOT_DPI +define+VERILATOR_COMMENTS"]) + +test.execute(use_libvpi=True) + +test.passes() diff --git a/test_regress/t/t_vpi_put_value_array.v b/test_regress/t/t_vpi_put_value_array.v new file mode 100644 index 000000000..f0aefe35b --- /dev/null +++ b/test_regress/t/t_vpi_put_value_array.v @@ -0,0 +1,76 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// Copyright 2024 by Diego Roux. 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 + +`ifdef VERILATOR_COMMENTS + `define PUBLIC_FLAT_RD /*verilator public_flat_rd*/ + `define PUBLIC_FLAT_RW /*verilator public_flat_rw*/ +`else + `define PUBLIC_FLAT_RD + `define PUBLIC_FLAT_RW +`endif + +module test (); + +`ifdef VERILATOR +`systemc_header +extern "C" int mon_check(); +`verilog +`endif + + reg [7:0] write_bytes [0:3] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_rl [3:0] `PUBLIC_FLAT_RW; + reg [7:0] write_bytes_nonzero_index [1:4] `PUBLIC_FLAT_RW; + + reg [15:0] write_shorts [0:3] `PUBLIC_FLAT_RW; + reg [31:0] write_words [0:3] `PUBLIC_FLAT_RW; + reg [63:0] write_longs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs [0:3] `PUBLIC_FLAT_RW; + reg [68:0] write_customs_nonzero_index_rl [4:1] `PUBLIC_FLAT_RW; + + integer write_integers [0:3] `PUBLIC_FLAT_RW; + + reg [7:0] write_scalar `PUBLIC_FLAT_RW; + reg [7:0] write_bounds [1:3] `PUBLIC_FLAT_RW; + reg [7:0] write_inaccessible [0:3] `PUBLIC_FLAT_RD; + +`ifdef IVERILOG + // stop icarus optimizing signals away + wire redundant = write_bytes[0][0] | + write_bytes[0][0] | + write_bytes_rl[0][0] | + write_bytes_nonzero_index[1][0] | + write_shorts[0][0] | + write_words[0][0] | + write_longs[0][0] | + write_customs[0][0] | + write_customs_nonzero_index_rl[1][0] | + write_integers[0][0] | + write_scalar[0] | + write_bounds[1][0] | + write_inaccessible[0][0]; +`endif + integer status; + + initial begin +`ifdef IVERILOG + status = $mon_check; +`endif + +`ifdef VERILATOR + status = $c32("mon_check()"); +`endif + + if (status != 0) begin + $write("%%Error: t_vpi_put_value_array.cpp:%0d: C Test failed\n", status); + $stop; + end + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_vpi_unimpl.cpp b/test_regress/t/t_vpi_unimpl.cpp index c6d73f324..732b30d53 100644 --- a/test_regress/t/t_vpi_unimpl.cpp +++ b/test_regress/t/t_vpi_unimpl.cpp @@ -57,8 +57,6 @@ int _mon_check_unimpl(p_cb_data cb_data) { vpi_get64(0, NULL); vpi_get_delays(NULL, NULL); vpi_put_delays(NULL, NULL); - vpi_get_value_array(NULL, NULL, NULL, 0); - vpi_put_value_array(NULL, NULL, NULL, 0); vpi_get_time(NULL, NULL); vpi_mcd_name(0); vpi_compare_objects(NULL, NULL); From cc1133c0d90988264a819d8248ab1c36a42c388f Mon Sep 17 00:00:00 2001 From: github action Date: Sat, 8 Feb 2025 00:07:38 +0000 Subject: [PATCH 66/93] Apply 'make format' --- test_regress/t/TestVpi.h | 1 + 1 file changed, 1 insertion(+) diff --git a/test_regress/t/TestVpi.h b/test_regress/t/TestVpi.h index 4c3bbc651..d2428ba4a 100644 --- a/test_regress/t/TestVpi.h +++ b/test_regress/t/TestVpi.h @@ -11,6 +11,7 @@ #include "sv_vpi_user.h" #include "vpi_user.h" + #include // Avoid C++11 in this file as not all simulators allow it From aa3942ab249f89610d8bc38e3dc0a2c4f04f1662 Mon Sep 17 00:00:00 2001 From: Trung Nguyen <57174311+trungnt2910@users.noreply.github.com> Date: Mon, 10 Feb 2025 14:45:50 +1100 Subject: [PATCH 67/93] Internals: `constexpr` for `VlUnpacked` data access (#5725) (#5770) Add `constexpr` to data access functions of `VlUnpacked` whose `std::array` equivalent in C++14 is `constexpr`. --- docs/CONTRIBUTORS | 1 + include/verilated_types.h | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 9fd2ac91e..5fffcca53 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -215,6 +215,7 @@ Todd Strader Tomasz Gorochowik Topa Topino Toru Niina +Trung Nguyen Tudor Timi Tymoteusz Blazejczyk Udi Finkelstein diff --git a/include/verilated_types.h b/include/verilated_types.h index c7ef5e35d..3f8b7006d 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -1319,7 +1319,7 @@ public: WData* data() { return &m_storage[0]; } const WData* data() const { return &m_storage[0]; } - std::size_t size() const { return N_Depth; } + constexpr std::size_t size() const { return N_Depth; } // To fit C++14 template int find_length(int dimension, std::false_type) const { @@ -1357,7 +1357,7 @@ public: } T_Value& operator[](size_t index) { return m_storage[index]; } - const T_Value& operator[](size_t index) const { return m_storage[index]; } + constexpr const T_Value& operator[](size_t index) const { return m_storage[index]; } // *this != that, which might be used for change detection/trigger computation, but avoid // operator overloading in VlUnpacked for safety in other contexts. From caa19c0cb3a12eba85a6c2f61e0ba82a811a84f0 Mon Sep 17 00:00:00 2001 From: Dave Sargeant Date: Mon, 10 Feb 2025 15:04:04 +0000 Subject: [PATCH 68/93] Fix VFileContent reference count (#5769) (#5771) --- docs/CONTRIBUTORS | 1 + src/V3FileLine.h | 13 +++++++++++++ 2 files changed, 14 insertions(+) diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 5fffcca53..6911b9e86 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -38,6 +38,7 @@ Daniel Bates David Horton David Ledger David Metz +Dave Sargeant David Stanford David Turner Dercury diff --git a/src/V3FileLine.h b/src/V3FileLine.h index e885cc7cd..13bbb8472 100644 --- a/src/V3FileLine.h +++ b/src/V3FileLine.h @@ -183,6 +183,19 @@ public: , m_filenameno{singleton().nameToNumber(filename)} , m_waive{false} , m_contentLineno{0} {} + explicit FileLine(const FileLine& from) + : m_msgEnIdx{from.m_msgEnIdx} + , m_filenameno{from.m_filenameno} + , m_waive{from.m_waive} + , m_contentLineno{from.m_contentLineno} + , m_firstLineno{from.m_firstLineno} + , m_firstColumn{from.m_firstColumn} + , m_lastLineno{from.m_lastLineno} + , m_lastColumn{from.m_lastColumn} + , m_contentp{from.m_contentp} + , m_parent{from.m_parent} { + if (m_contentp) m_contentp->refInc(); + } explicit FileLine(FileLine* fromp) : m_msgEnIdx{fromp->m_msgEnIdx} , m_filenameno{fromp->m_filenameno} From f753ae25188b5aa70aaecd4fe87ab67b32e4368d Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Tue, 11 Feb 2025 18:06:34 +0100 Subject: [PATCH 69/93] Support selects on arbitrary string expressions (#5773) --- src/V3WidthSel.cpp | 16 ++++++--- test_regress/t/t_string_sel.py | 18 ++++++++++ test_regress/t/t_string_sel.v | 60 ++++++++++++++++++++++++++++++++++ 3 files changed, 89 insertions(+), 5 deletions(-) create mode 100755 test_regress/t/t_string_sel.py create mode 100644 test_regress/t/t_string_sel.v diff --git a/src/V3WidthSel.cpp b/src/V3WidthSel.cpp index 7073cf8d4..4bb38eb1f 100644 --- a/src/V3WidthSel.cpp +++ b/src/V3WidthSel.cpp @@ -305,13 +305,19 @@ class WidthSelVisitor final : public VNVisitor { VL_DO_DANGLING(pushDeletep(nodep), nodep); } else if (VN_IS(ddtypep, BasicDType) && ddtypep->isString()) { // SELBIT(string, index) -> GETC(string, index) - const AstNodeVarRef* const varrefp = VN_CAST(fromp, NodeVarRef); - if (!varrefp) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: String array operation on non-variable"); + AstNodeExpr* exprp = fromp; + while (true) { + if (AstMemberSel* const memberselp = VN_CAST(exprp, MemberSel)) { + exprp = memberselp->fromp(); + } else if (AstStructSel* const structselp = VN_CAST(exprp, StructSel)) { + exprp = structselp->fromp(); + } else { + break; + } } + const AstNodeVarRef* const varrefp = VN_CAST(exprp, NodeVarRef); AstNodeExpr* newp; - if (varrefp && varrefp->access().isReadOnly()) { + if (!varrefp || varrefp->access().isReadOnly()) { newp = new AstGetcN{nodep->fileline(), fromp, rhsp}; } else { newp = new AstGetcRefN{nodep->fileline(), fromp, rhsp}; diff --git a/test_regress/t/t_string_sel.py b/test_regress/t/t_string_sel.py new file mode 100755 index 000000000..b7a43ed66 --- /dev/null +++ b/test_regress/t/t_string_sel.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_string_sel.v b/test_regress/t/t_string_sel.v new file mode 100644 index 000000000..acf7478e9 --- /dev/null +++ b/test_regress/t/t_string_sel.v @@ -0,0 +1,60 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +typedef struct { + string str; +} str_s; + +class c; + string str; + function new(); + str = "foo"; + endfunction + function string get_str(); + return str; + endfunction +endclass + +module t (/*AUTOARG*/); + string str = "bar"; + + function string get_str(); + return str; + endfunction + + initial begin + c o = new; + str_s st = '{"qux"}; + string sc = {"foo", "bar"}; + + // read + if (str[0] != "b") $stop; + if (get_str()[1] != "a") $stop; + if (str[3] != "\0") $stop; + if (st.str[2] != "x") $stop; + if (st.str[99] != "\0") $stop; + if (o.str[0] != "f") $stop; + if (o.get_str()[1] != "o") $stop; + if (o.str[-1] != "\0") $stop; + if (sc[2] != "o") $stop; + if ($sformatf("foo%s", "bar")[3] != "b") $stop; + if (sc[-1] != "\0") $stop; + if (sc[6] != "\0") $stop; + if (sc[99] != "\0") $stop; + + // write + sc[5] = "z"; + if (sc != "foobaz") $stop; + o.str[0] = "b"; + if (o.str != "boo") $stop; + st.str[2] = "z"; + if (st.str != "quz") $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From ffb02cea154805b0dd4924d60948b5be07a65707 Mon Sep 17 00:00:00 2001 From: Krzysztof Bieganski Date: Fri, 14 Feb 2025 00:33:12 +0100 Subject: [PATCH 70/93] Fix ignoring joins in stringify in preprocessor (#5777) Signed-off-by: Krzysztof Bieganski --- src/V3PreProc.cpp | 5 +++++ test_regress/t/t_preproc_strify_join.out | 3 +++ test_regress/t/t_preproc_strify_join.py | 24 ++++++++++++++++++++++++ test_regress/t/t_preproc_strify_join.v | 19 +++++++++++++++++++ 4 files changed, 51 insertions(+) create mode 100644 test_regress/t/t_preproc_strify_join.out create mode 100755 test_regress/t/t_preproc_strify_join.py create mode 100644 test_regress/t/t_preproc_strify_join.v diff --git a/src/V3PreProc.cpp b/src/V3PreProc.cpp index fa41f5582..435fe64c2 100644 --- a/src/V3PreProc.cpp +++ b/src/V3PreProc.cpp @@ -1065,6 +1065,11 @@ int V3PreProcImp::getStateToken() { // FALLTHRU, handle as with VP_SYMBOL_JOIN } } + if (state() == ps_STRIFY) { + // Ignore joins and symbol joins in stringify as they don't affect the final string + if (tok == VP_JOIN) goto next_tok; + if (tok == VP_SYMBOL_JOIN) tok = VP_SYMBOL; + } if (tok == VP_SYMBOL_JOIN // not else if, can fallthru from above if() || tok == VP_DEFREF_JOIN || tok == VP_JOIN) { // a`` -> string doesn't include the ``, so can just grab next and continue diff --git a/test_regress/t/t_preproc_strify_join.out b/test_regress/t/t_preproc_strify_join.out new file mode 100644 index 000000000..b80acd35b --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.out @@ -0,0 +1,3 @@ +"foo-bar-qux" +""foo-bar-qux"" +"" diff --git a/test_regress/t/t_preproc_strify_join.py b/test_regress/t/t_preproc_strify_join.py new file mode 100755 index 000000000..981d60faf --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +stdout_filename = os.path.join(test.obj_dir, test.name + "__test.vpp") + +test.compile(verilator_flags2=['-E -P'], + verilator_make_gmake=False, + make_top_shell=False, + make_main=False, + stdout_filename=stdout_filename) + +test.files_identical(stdout_filename, test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_preproc_strify_join.v b/test_regress/t/t_preproc_strify_join.v new file mode 100644 index 000000000..88241fae5 --- /dev/null +++ b/test_regress/t/t_preproc_strify_join.v @@ -0,0 +1,19 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +`define FOO foo +`define BAR bar +`define QUX qux +`define STRIFY `"`FOO``-```BAR``-```QUX```" + +`define NESTED_STRIFY `"`STRIFY```" + +`define EMPTY +`define EMPTY_STRIFY `"`EMPTY```" + +`STRIFY +`NESTED_STRIFY +`EMPTY_STRIFY From b645fcf0273d299bfc4b4f55bf928d699d5c4a4e Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Fri, 14 Feb 2025 14:58:11 -0500 Subject: [PATCH 71/93] More V3EmitV support (#5779) --- src/V3EmitV.cpp | 21 ++++++++++++++++++++- test_regress/t/t_debug_emitv.out | 28 ++++++++++++++++++++++++++++ test_regress/t/t_debug_emitv.v | 20 ++++++++++++++++++++ 3 files changed, 68 insertions(+), 1 deletion(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 8aab51688..0cc600fab 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -235,6 +235,18 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { void visit(AstCoverInc*) override {} // N/A void visit(AstCoverToggle*) override {} // N/A + void visit(AstTestPlusArgs* nodep) override { + putfs(nodep, nodep->verilogKwd()); + putbs("("); + iterateChildrenConst(nodep); + puts(")"); + } + void visit(AstValuePlusArgs* nodep) override { + putfs(nodep, nodep->verilogKwd()); + putbs("("); + iterateChildrenConst(nodep); + puts(")"); + } void visitNodeDisplay(AstNode* nodep, AstNode* fileOrStrgp, const string& text, AstNode* exprsp) { putfs(nodep, nodep->verilogKwd()); @@ -476,7 +488,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { bool inPct = false; putbs(""); for (const char c : format) { - if (c == '%') { + if (!inPct && c == '%') { inPct = true; } else if (!inPct) { // Normal text string s; @@ -676,6 +688,13 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { iterateAndNextConstNull(nodep->itemsp()); puts("}"); } + void visit(AstEnumItemRef* nodep) override { + if (AstNodeModule* const classOrPackagep = nodep->classOrPackagep()) { + putfs(nodep, classOrPackagep->prettyName()); + puts("::"); + } + putfs(nodep, nodep->name()); + } void visit(AstEnumItem* nodep) override { putfs(nodep, nodep->name()); iterateConstNull(nodep->rangep()); diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index d7337c931..efa089909 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -23,10 +23,30 @@ module Vt_debug_emitv_t; union { logic a; } unu; + integer signed i1; int signed array[0:2]; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end + initial begin + begin + if ($test$plusargs(40'h48454c4c4f)) begin + $display("Hello argument found."); + end + if ((Pkg::FOO == 'sh0)) begin + $write(""); + end + if ((ZERO == 'sh0)) begin + $write(""); + end + if ($value$plusargs(56'h544553543d2564i1)) begin + $display("value was %~", i1); + end + else begin + $display("+TEST= not found"); + end + end + end bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; logic [15:0] pubflat; logic [15:0] pubflat_r; @@ -120,6 +140,8 @@ module Vt_debug_emitv_t; int signed sum; real r; string str; + int signed mod_val; + int signed mod_res; always @(posedge clk) begin begin cyc <= (cyc + 'sh1); @@ -240,6 +262,7 @@ module Vt_debug_emitv_t; $stop; end sub.r = 62.0; + mod_res = (mod_val % 'sh5); $display("%g", $log10(r)); $display("%g", $ln(r)); $display("%g", $exp(r)); @@ -308,4 +331,9 @@ package Vt_debug_emitv_p; endpackage package Vt_debug_emitv_Pkg; logic signed [31:0] PKG_PARAM; + typedef enum int signed{ + FOO = 32'h0, + BAR = 32'h1, + BAZ = 32'h2 + } enum_t; endpackage diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index 8c7ba061a..c6cad71ea 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -7,6 +7,12 @@ package Pkg; localparam PKG_PARAM = 1; + + typedef enum int { + FOO = 0, + BAR, + BAZ + } enum_t; endpackage package PkgImp; import Pkg::*; @@ -52,8 +58,18 @@ module t (/*AUTOARG*/ us_t us; union_t unu; + integer i1; int array[3]; initial array = '{1,2,3}; + initial begin + if ($test$plusargs("HELLO")) $display("Hello argument found."); + if (Pkg::FOO == 0) $write(""); + if (ZERO == 0) $write(""); + if ($value$plusargs("TEST=%d", i1)) + $display("value was %d", i1); + else + $display("+TEST= not found"); + end bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; @@ -125,6 +141,8 @@ module t (/*AUTOARG*/ int sum; real r; string str; + int mod_val; + int mod_res; always_ff @ (posedge clk) begin cyc <= cyc + 1; r <= r + 0.01; @@ -195,6 +213,8 @@ module t (/*AUTOARG*/ if (Pkg::PKG_PARAM != 1) $stop; sub.r = 62.0; + mod_res = mod_val % 5; + $display("%g", $log10(r)); $display("%g", $ln(r)); $display("%g", $exp(r)); From 6940ee420da886f33c4b9e77aa22ae00fbefd776 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 15 Feb 2025 00:49:13 -0500 Subject: [PATCH 72/93] Commentary --- docs/CONTRIBUTING.rst | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/CONTRIBUTING.rst b/docs/CONTRIBUTING.rst index c7a4de79d..061394812 100644 --- a/docs/CONTRIBUTING.rst +++ b/docs/CONTRIBUTING.rst @@ -7,8 +7,8 @@ This contributing document contains some suggestions that may make contributions flow more efficiently. -Did you find a bug? -------------------- +Did you find a Verilator bug? +----------------------------- - Please **ensure the bug was not already reported** by searching `Verilator Issues `__. @@ -28,8 +28,8 @@ Did you find a bug? `__. -Did you write a patch that fixes a bug? ---------------------------------------- +Did you write a patch that fixes a Verilator bug? +------------------------------------------------- - Please `Open a new Verilator issue `__ if there is not one already describing the bug. @@ -72,8 +72,8 @@ Did you write a patch that fixes a bug? - Most important is we get your patch. -Do you have questions? ----------------------- +Do you have questions on Verilator? +----------------------------------- - Please see FAQ section and rest of the `Verilator manual `__, or `Verilator From 754c5c71f1683032ce6e36c24ab8c9aeb28a87a2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 15 Feb 2025 00:52:41 -0500 Subject: [PATCH 73/93] Fix force VPI public signal visibility (#5225). --- Changes | 1 + src/V3EmitCSyms.cpp | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index db6a674f5..2b2c13540 100644 --- a/Changes +++ b/Changes @@ -28,6 +28,7 @@ Verilator 5.033 devel * Optimize labels as final `if` block statements (#5744). * Optimize empty function definition bodies (#5750). * Fix error message when call task as a function (#3089). [Matthew Ballance] +* Fix force VPI public signal visibility (#5225). [Frédéric Requin] * Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] * Fix constrained random for > 64-bit associative arrays (#5670) (#5682). [Yilou Wang] * Fix mis-optimizing away `$urandom` (#5703). [Parker Schless] diff --git a/src/V3EmitCSyms.cpp b/src/V3EmitCSyms.cpp index 1eaeea18c..b92b0dcef 100644 --- a/src/V3EmitCSyms.cpp +++ b/src/V3EmitCSyms.cpp @@ -369,7 +369,8 @@ class EmitCSyms final : EmitCBaseVisitorConst { void visit(AstVar* nodep) override { nameCheck(nodep); iterateChildrenConst(nodep); - if (nodep->isSigUserRdPublic() && !m_cfuncp) m_modVars.emplace_back(m_modp, nodep); + if ((nodep->isSigUserRdPublic() || nodep->isSigUserRWPublic()) && !m_cfuncp) + m_modVars.emplace_back(m_modp, nodep); } void visit(AstVarScope* nodep) override { iterateChildrenConst(nodep); From fbe8439eb8a6aec27c600797b8995eb91835e369 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 15 Feb 2025 00:55:01 -0500 Subject: [PATCH 74/93] Commentary: Changes update --- Changes | 7 +++++++ docs/spelling.txt | 1 + 2 files changed, 8 insertions(+) diff --git a/Changes b/Changes index 2b2c13540..c8d830751 100644 --- a/Changes +++ b/Changes @@ -15,12 +15,16 @@ Verilator 5.033 devel * Add `COVERIGN` warning, as a more specific UNSUPPORTED error. * Add `--public-ignore` to ignore public metacomments (#5716). [Andrew Nolte] +* Add `--preproc-token-limit` (#5768). [Krzysztof Bieganski, Antmicro Ltd.] * Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton] * Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki] +* Support VPI array accessors (#5612). [Moubarak Jeje] +* Support unpacked structure constrained randomization (#5657) (#5759). [Yilou Wang] * Support generated classes (#5665). [Shou-Li Hsu] * Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang] * Support `+incdir` with multiple directories. * Support integer atom type ports in `--hierarchical` (#5748). [Bartłomiej Chmiel, Antmicro Ltd.] +* Support selects on arbitrary string expressions (#5773). [Krzysztof Bieganski, Antmicro Ltd.] * Support side effects of form 'variable[index_function()]++'. * Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.] * Improve VPI write errors (#5712). [Andrew Nolte] @@ -43,6 +47,9 @@ Verilator 5.033 devel * Fix inlined expression with assignment under LHS of NBA (#5736) (#5740). [Geza Lore] * Fix duplicate-named class variable equivalence (#5737). * Fix `-j` ignored after `-f` (#5749). [Luca Colagrande] +* Fix handling forced assigns in V3Life (#5757). [Bartłomiej Chmiel, Antmicro Ltd.] +* Fix VFileContent reference count (#5769) (#5771). [Dave Sargeant] +* Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.] * Fix matching language extension options including dots. diff --git a/docs/spelling.txt b/docs/spelling.txt index ca586f3dd..fc2dd5b6e 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -902,6 +902,7 @@ predefines prepareClone prepend prepended +preproc preprocess preprocessed preprocessing From c840ffb0ae8fe5cce69aba9b608e9e60af8668f9 Mon Sep 17 00:00:00 2001 From: Kamil Rakoczy Date: Mon, 17 Feb 2025 13:47:41 +0100 Subject: [PATCH 75/93] Support nested classes (#4178) (#5778) Signed-off-by: Kamil Rakoczy --- src/V3LinkDot.cpp | 43 +++++++++- src/V3WidthCommit.cpp | 10 ++- src/verilog.y | 3 +- test_regress/t/t_class_class.out | 5 -- test_regress/t/t_class_class.py | 4 +- test_regress/t/t_class_class.v | 3 + test_regress/t/t_class_compare.v | 14 ++++ test_regress/t/t_class_extends1.v | 43 ++++++++++ test_regress/t/t_class_extends2.v | 46 +++++++++++ test_regress/t/t_class_extends_param.v | 35 ++++++++ test_regress/t/t_class_extends_this.v | 35 +++++++- test_regress/t/t_class_extends_this3.v | 10 +++ test_regress/t/t_class_extern.v | 50 +++++++++++- test_regress/t/t_class_local.v | 53 ++++++++++++ test_regress/t/t_class_local_nested_bad.out | 9 +++ test_regress/t/t_class_local_nested_bad.py | 16 ++++ test_regress/t/t_class_local_nested_bad.v | 25 ++++++ test_regress/t/t_class_module.v | 11 +++ test_regress/t/t_class_nested.py | 18 +++++ test_regress/t/t_class_nested.v | 90 +++++++++++++++++++++ test_regress/t/t_class_new.v | 15 ++++ test_regress/t/t_class_param.v | 11 +++ test_regress/t/t_class_param_rewrite.v | 8 ++ test_regress/t/t_class_static_member.v | 8 +- test_regress/t/t_class_virtual.v | 24 ++++++ test_regress/t/t_implements_nested.py | 16 ++++ test_regress/t/t_implements_nested.v | 16 ++++ test_regress/t/t_implements_nested_bad.out | 7 +- test_regress/t/t_implements_nested_bad.v | 4 +- test_regress/t/t_semaphore_class_nested.py | 16 ++++ test_regress/t/t_semaphore_class_nested.v | 39 +++++++++ 31 files changed, 664 insertions(+), 23 deletions(-) delete mode 100644 test_regress/t/t_class_class.out create mode 100644 test_regress/t/t_class_local_nested_bad.out create mode 100755 test_regress/t/t_class_local_nested_bad.py create mode 100644 test_regress/t/t_class_local_nested_bad.v create mode 100755 test_regress/t/t_class_nested.py create mode 100644 test_regress/t/t_class_nested.v create mode 100755 test_regress/t/t_implements_nested.py create mode 100644 test_regress/t/t_implements_nested.v create mode 100755 test_regress/t/t_semaphore_class_nested.py create mode 100644 test_regress/t/t_semaphore_class_nested.v diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 9f77c8aad..b1b0b9223 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1013,6 +1013,7 @@ class LinkDotFindVisitor final : public VNVisitor { VL_RESTORER(m_modBlockNum); VL_RESTORER(m_modWithNum); VL_RESTORER(m_modArgNum); + VL_RESTORER(m_explicitNew); { UINFO(4, " Link Class: " << nodep << endl); VSymEnt* const upperSymp = m_curSymp; @@ -1176,12 +1177,39 @@ class LinkDotFindVisitor final : public VNVisitor { } // Change to appropriate package if extern declaration (vs definition) if (nodep->classOrPackagep()) { + // class-in-class + AstDot* const dotp = VN_CAST(nodep->classOrPackagep(), Dot); AstClassOrPackageRef* const cpackagerefp = VN_CAST(nodep->classOrPackagep(), ClassOrPackageRef); - if (!cpackagerefp) { - nodep->v3warn(E_UNSUPPORTED, - "Unsupported: extern function definition with class-in-class"); - } else { + if (dotp) { + AstClassOrPackageRef* const lhsp = VN_AS(dotp->lhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, lhsp, false, + "External definition :: reference"); + AstClass* const lhsclassp = VN_CAST(lhsp->classOrPackageSkipp(), Class); + if (!lhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(lhsclassp); + upSymp = m_curSymp; + AstClassOrPackageRef* const rhsp = VN_AS(dotp->rhsp(), ClassOrPackageRef); + m_statep->resolveClassOrPackage(m_curSymp, rhsp, false, + "External definition :: reference"); + AstClass* const rhsclassp = VN_CAST(rhsp->classOrPackageSkipp(), Class); + if (!rhsclassp) { + nodep->v3error("Extern declaration's scope is not a defined class"); + } else { + m_curSymp = m_statep->getNodeSym(rhsclassp); + upSymp = m_curSymp; + if (!nodep->isExternDef()) { + // Move it to proper spot under the target class + nodep->unlinkFrBack(); + rhsclassp->addStmtsp(nodep); + nodep->isExternDef(true); // So we check there's a matching extern + nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); + } + } + } + } else if (cpackagerefp) { if (!cpackagerefp->classOrPackageSkipp()) { m_statep->resolveClassOrPackage(m_curSymp, cpackagerefp, false, "External definition :: reference"); @@ -1200,6 +1228,8 @@ class LinkDotFindVisitor final : public VNVisitor { nodep->classOrPackagep()->unlinkFrBack()->deleteTree(); } } + } else { + v3fatalSrc("Unhandled extern function definition package"); } } // Set the class as package for iteration @@ -4007,6 +4037,11 @@ class LinkDotResolveVisitor final : public VNVisitor { LINKDOT_VISIT_START(); UINFO(5, indent() << "visit " << nodep << endl); checkNoDot(nodep); + AstClass* const topclassp = VN_CAST(m_modp, Class); + if (nodep->isInterfaceClass() && topclassp && topclassp->isInterfaceClass()) { + nodep->v3error("Interface class shall not be nested within another interface class." + " (IEEE 1800-2023 8.26)"); + } VL_RESTORER(m_curSymp); VL_RESTORER(m_modSymp); VL_RESTORER(m_modp); diff --git a/src/V3WidthCommit.cpp b/src/V3WidthCommit.cpp index 5050fda8d..0318294e8 100644 --- a/src/V3WidthCommit.cpp +++ b/src/V3WidthCommit.cpp @@ -100,9 +100,15 @@ private: if (local || prot) { const auto refClassp = VN_CAST(m_modp, Class); const char* how = nullptr; - if (local && defClassp && refClassp != defClassp) { + // Inner nested classes can access `local` or `protected` members of their outer class + const auto nestedAccess = [refClassp](const AstClass*, const AstNode* memberp) { + return memberp == refClassp; + }; + if (local && defClassp + && ((refClassp != defClassp) && !(defClassp->existsMember(nestedAccess)))) { how = "'local'"; - } else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp)) { + } else if (prot && defClassp && !AstClass::isClassExtendedFrom(refClassp, defClassp) + && !(defClassp->existsMember(nestedAccess))) { how = "'protected'"; } if (how) { diff --git a/src/verilog.y b/src/verilog.y index 3d66945f0..c1e5a51c4 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -7354,8 +7354,7 @@ class_item: // ==IEEE: class_item | class_method { $$ = $1; } | class_constraint { $$ = $1; } // - | class_declaration - { $$ = nullptr; BBUNSUP($1, "Unsupported: class within class"); } + | class_declaration { $$ = $1; } | timeunits_declaration { $$ = $1; } | covergroup_declaration { $$ = nullptr; BBCOVERIGN($1, "Ignoring unsupported: covergroup within class"); } diff --git a/test_regress/t/t_class_class.out b/test_regress/t/t_class_class.out deleted file mode 100644 index 2ec79ad39..000000000 --- a/test_regress/t/t_class_class.out +++ /dev/null @@ -1,5 +0,0 @@ -%Error-UNSUPPORTED: t/t_class_class.v:12:4: Unsupported: class within class - 12 | class SubCls; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest -%Error: Exiting due to diff --git a/test_regress/t/t_class_class.py b/test_regress/t/t_class_class.py index 710a094ab..d4f986441 100755 --- a/test_regress/t/t_class_class.py +++ b/test_regress/t/t_class_class.py @@ -11,6 +11,8 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) +test.compile() + +test.execute() test.passes() diff --git a/test_regress/t/t_class_class.v b/test_regress/t/t_class_class.v index bf1ae7da4..14177f768 100644 --- a/test_regress/t/t_class_class.v +++ b/test_regress/t/t_class_class.v @@ -31,5 +31,8 @@ module t (/*AUTOARG*/); if (c.imemberb != 20) $stop; if (c.sc.smembera != 30) $stop; if (c.sc.smemberb != 40) $stop; + + $write("*-* All Finished *-*\n"); + $finish; end endmodule diff --git a/test_regress/t/t_class_compare.v b/test_regress/t/t_class_compare.v index bcbb1842a..b20d435cd 100644 --- a/test_regress/t/t_class_compare.v +++ b/test_regress/t/t_class_compare.v @@ -11,6 +11,9 @@ `define check_ne(lhs, rhs) `check_comp(lhs, rhs, ==, 1'b0) `check_comp(lhs, rhs, !=, 1'b1) class Cls; + class InnerCls; + int j; + endclass int i; endclass @@ -22,14 +25,25 @@ module t; Cls a = new; Cls b = new; ExtendCls ext = new; + Cls::InnerCls ia = new; + Cls::InnerCls ib = new; + ExtendCls::InnerCls iext = new; `check_ne(a, b) `check_ne(a, ext) `check_ne(ext, a) + `check_ne(ia, ib) + `check_ne(ia, iext) + `check_ne(iext, ia) a = b; + ia = ib; `check_eq(a, b) + `check_eq(ia, ib) a = ext; + ia = iext; `check_eq(a, ext) `check_eq(ext, a) + `check_eq(ia, iext) + `check_eq(iext, ia) $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_extends1.v b/test_regress/t/t_class_extends1.v index c0f8ceeec..afe7878d9 100644 --- a/test_regress/t/t_class_extends1.v +++ b/test_regress/t/t_class_extends1.v @@ -5,21 +5,55 @@ // SPDX-License-Identifier: CC0-1.0 class Base0; + class BaseInnerOnly; + int inneronly; + function new(); + inneronly = 10; + if (inneronly != 10) $stop; + endfunction + endclass + + class BaseInnerOver; + int innerover; + function new(); + innerover = 10; + if (innerover != 10) $stop; + endfunction + endclass + int baseonly; int baseover; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; function void b_set_bo(int v); baseover = v; endfunction function int b_get_bo(); return baseover; endfunction function int get_bo(); return baseover; endfunction + function void b_set_io(int v); innerover.innerover = v; endfunction + function int b_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass class Ext extends Base0; + class BaseInnerOver; + int innerover; + function new(); + innerover = 20; + if (innerover != 20) $stop; + endfunction + endclass + int baseover; int extonly; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; function void e_set_bo(int v); baseover = v; endfunction function int e_get_bo(); return baseover; endfunction function int get_bo(); return baseover; endfunction + function void e_set_io(int v); innerover.innerover = v; endfunction + function int e_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass module t (/*AUTOARG*/); @@ -29,15 +63,24 @@ module t (/*AUTOARG*/); c.baseonly = 10; c.baseover = 20; c.extonly = 30; + c.inneronly.inneronly = 40; + c.innerover.innerover = 50; if (c.baseonly != 10) $stop; if (c.baseover != 20) $stop; if (c.extonly != 30) $stop; + if (c.inneronly.inneronly != 40) $stop; + if (c.innerover.innerover != 50) $stop; c.b_set_bo(100); c.e_set_bo(200); + c.b_set_io(300); + c.e_set_io(400); if (c.b_get_bo() != 100) $stop; if (c.e_get_bo() != 200) $stop; if (c.get_bo() != 200) $stop; + if (c.b_get_io() != 300) $stop; + if (c.e_get_io() != 400) $stop; + if (c.get_io() != 400) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends2.v b/test_regress/t/t_class_extends2.v index 8c30f0c6b..14d50907b 100644 --- a/test_regress/t/t_class_extends2.v +++ b/test_regress/t/t_class_extends2.v @@ -6,22 +6,59 @@ package Pkg; class Base0; + class BaseInnerOnly; + int inneronly; + function new(); + inneronly = 10; + if (inneronly != 10) $stop; + endfunction + endclass + + class BaseInnerOver; + int innerover; + function new(); + innerover = 10; + if (innerover != 10) $stop; + endfunction + endclass + int baseonly; int baseover; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; function void b_set_bo(int v); baseover = v; endfunction function int b_get_bo(); return baseover; endfunction function int get_bo(); return baseover; endfunction + function void b_set_io(int v); innerover.innerover = v; endfunction + function int b_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass endpackage +// We need to import Base0, as verilator currently doesn't support +// multiple `::` references, but we would need to do that to reference +// `BaseInnerOnly` class inside `Ext` class. +import Pkg::Base0; class Ext extends Pkg::Base0; + class BaseInnerOver; + int innerover; + function new(); + innerover = 20; + if (innerover != 20) $stop; + endfunction + endclass int baseover; int extonly; + BaseInnerOnly inneronly = new; + BaseInnerOver innerover = new; function void e_set_bo(int v); baseover = v; endfunction function int e_get_bo(); return baseover; endfunction function int get_bo(); return baseover; endfunction + function void e_set_io(int v); innerover.innerover = v; endfunction + function int e_get_io(); return innerover.innerover; endfunction + function int get_io(); return innerover.innerover; endfunction endclass module t (/*AUTOARG*/); @@ -31,15 +68,24 @@ module t (/*AUTOARG*/); c.baseonly = 10; c.baseover = 20; c.extonly = 30; + c.inneronly.inneronly = 40; + c.innerover.innerover = 50; if (c.baseonly != 10) $stop; if (c.baseover != 20) $stop; if (c.extonly != 30) $stop; + if (c.inneronly.inneronly != 40) $stop; + if (c.innerover.innerover != 50) $stop; c.b_set_bo(100); c.e_set_bo(200); + c.b_set_io(300); + c.e_set_io(400); if (c.b_get_bo() != 100) $stop; if (c.e_get_bo() != 200) $stop; if (c.get_bo() != 200) $stop; + if (c.b_get_io() != 300) $stop; + if (c.e_get_io() != 400) $stop; + if (c.get_io() != 400) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends_param.v b/test_regress/t/t_class_extends_param.v index f84fe1fa2..f8544aa80 100644 --- a/test_regress/t/t_class_extends_param.v +++ b/test_regress/t/t_class_extends_param.v @@ -8,26 +8,53 @@ module t (/*AUTOARG*/ ); class Foo; + class InnerFoo; + int y = 10; + function int get_y; + return y; + endfunction + function int get_30; + return 30; + endfunction + endclass + int x = 1; + InnerFoo foo = new; function int get_x; return x; endfunction function int get_3; return 3; endfunction + function InnerFoo get_foo; + return foo; + endfunction endclass class Bar #(type T=Foo) extends T; endclass class Baz; + class InnerFoo; + int y = 20; + function int get_y; + return y; + endfunction + function int get_40; + return 40; + endfunction + endclass int x = 2; + InnerFoo foo = new; function int get_x; return x; endfunction function int get_4; return 4; endfunction + function InnerFoo get_foo; + return foo; + endfunction endclass class ExtendBar extends Bar#(); @@ -71,6 +98,9 @@ module t (/*AUTOARG*/ function int get_x_of_item(int i); return q[i].x; endfunction + function int get_y_of_item(int i); + return q[i].get_foo().get_y(); + endfunction endclass Bar #() bar_foo_i; @@ -93,8 +123,12 @@ module t (/*AUTOARG*/ if (bar_foo_i.get_x() != 1) $stop; if (bar_foo_i.get_3() != 3) $stop; + if (bar_foo_i.get_foo().get_y() != 10) $stop; + if (bar_foo_i.get_foo().get_30() != 30) $stop; if (bar_baz_i.get_x() != 2) $stop; if (bar_baz_i.get_4() != 4) $stop; + if (bar_baz_i.get_foo().get_y() != 20) $stop; + if (bar_baz_i.get_foo().get_40() != 40) $stop; if (extend_bar_i.get_x() != 1) $stop; if (extend_bar_i.get_6() != 6) $stop; if (extend_bar_i.get_x() != 1) $stop; @@ -106,6 +140,7 @@ module t (/*AUTOARG*/ if (extend_extend_bar_i.get_x() != 1) $stop; if (extend_extend_bar_i.get_12() != 12) $stop; if (extend_foo_dict_i.get_x_of_item(1) != 1) $stop; + if (extend_foo_dict_i.get_y_of_item(1) != 10) $stop; $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_extends_this.v b/test_regress/t/t_class_extends_this.v index fb185267d..00fd653b9 100644 --- a/test_regress/t/t_class_extends_this.v +++ b/test_regress/t/t_class_extends_this.v @@ -7,7 +7,20 @@ typedef class Cls; class Base; + class BaseInner; + int value = 10; + function void test; + if (value != 10) $stop; + if (this.value != 10) $stop; + value = 20; + if (value != 20) $stop; + this.value = 30; + if (value != 30) $stop; + endfunction + endclass + int value = 1; + BaseInner inner = new; function void test; if (value != 1) $stop; if (this.value != 1) $stop; @@ -19,7 +32,25 @@ class Base; endclass class Cls extends Base; + class BaseInner extends Base::BaseInner; + int value = 100; + function void test; + if (value != 100) $stop; + if (this.value != 100) $stop; + if (super.value != 10) $stop; + super.test(); + if (value != 100) $stop; + if (this.value != 100) $stop; + if (super.value != 30) $stop; + value = 200; + if (value != 200) $stop; + this.value = 300; + if (value != 300) $stop; + endfunction + endclass + int value = 20; + BaseInner inner = new; function void test; if (value != 20) $stop; if (this.value != 20) $stop; @@ -31,7 +62,9 @@ class Cls extends Base; super.value = 9; this.value = 29; if (super.value != 9) $stop; - if (value != 29) $stop;; + if (value != 29) $stop; + + inner.test(); endfunction endclass diff --git a/test_regress/t/t_class_extends_this3.v b/test_regress/t/t_class_extends_this3.v index 20a66e469..908276e58 100644 --- a/test_regress/t/t_class_extends_this3.v +++ b/test_regress/t/t_class_extends_this3.v @@ -7,15 +7,24 @@ typedef class Cls; class Base; + class Inner; + int value = 10; + function void testBaseInner; + if (value != 10) $stop; + endfunction + endclass int value = 1; + Inner inner = new; function void testBase; if (value != 1) $stop; + if (inner.value != 10) $stop; endfunction endclass class Cls extends Base; function void testDerived; if (value != 1) $stop; + if (inner.value != 10) $stop; endfunction endclass @@ -25,6 +34,7 @@ module t (/*AUTOARG*/); c = new; c.testBase(); c.testDerived(); + c.inner.testBaseInner(); $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_extern.v b/test_regress/t/t_class_extern.v index 0cddfdc3f..3afa91045 100644 --- a/test_regress/t/t_class_extern.v +++ b/test_regress/t/t_class_extern.v @@ -13,6 +13,16 @@ class Cls; extern task ext_t_np; extern task ext_t_p(); extern task ext_t_i(int in); + class SubCls; + int value; + extern function int ext_f_np; + extern function int ext_f_p(); + extern function int ext_f_i(int in); + extern static function int get_10(); + extern task ext_t_np; + extern task ext_t_p(); + extern task ext_t_i(int in); + endclass endclass function int Cls::ext_f_np; @@ -42,15 +52,51 @@ task Cls::ext_t_i(int in); value = in; endtask +function int Cls::SubCls::ext_f_np; + return 10; +endfunction + +function int Cls::SubCls::ext_f_p(); + return value; +endfunction + +function int Cls::SubCls::ext_f_i(int in); + return in+10; +endfunction + +function int Cls::SubCls::get_10(); + return 10; +endfunction + +task Cls::SubCls::ext_t_np(); + $write("Cls::SubCls::ext_t_np\n"); +endtask +task Cls::SubCls::ext_t_p(); + $write("Cls::SubCls::ext_t_p\n"); +endtask +task Cls::SubCls::ext_t_i(int in); + if (in != 20) $stop; + value = in; +endtask + + module t (/*AUTOARG*/); initial begin Cls c = new; + Cls::SubCls subc = new; c.ext_t_i(2); - c.ext_t_np(); - c.ext_t_p(); if (c.ext_f_np() != 1) $stop; if (c.ext_f_p() != 2) $stop; if (c.ext_f_i(10) != 11) $stop; if (Cls::get_1() != 1) $stop; + subc.ext_t_i(20); + if (subc.ext_f_np() != 10) $stop; + if (subc.ext_f_p() != 20) $stop; + if (subc.ext_f_i(11) != 21) $stop; + if (Cls::SubCls::get_10() != 10) $stop; + subc.ext_t_np(); + subc.ext_t_p(); + c.ext_t_np(); + c.ext_t_p(); end endmodule diff --git a/test_regress/t/t_class_local.v b/test_regress/t/t_class_local.v index 0a5b6cf78..db9d9e975 100644 --- a/test_regress/t/t_class_local.v +++ b/test_regress/t/t_class_local.v @@ -31,6 +31,34 @@ class Cls; Cls::s_loc(); // Ok Cls::s_prot(); // Ok endtask + class InnerCls; + typedef enum {A = 10, B = 20, C = 30} en_t; + + int m_pub = 1; + local int m_loc = 2; + protected int m_prot = B; + task f_pub; endtask + local task f_loc; endtask + protected task f_prot; endtask + static task s_pub; endtask + static local task s_loc; endtask + static protected task s_prot; endtask + task check; + Cls o; + if (m_pub != 1) $stop; + if (m_loc != 2) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_loc(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_loc(); // Ok + s_prot(); // Ok + Cls::s_pub(); // Ok + Cls::s_loc(); // Ok + Cls::s_prot(); // Ok + endtask + endclass endclass class Ext extends Cls; @@ -44,27 +72,52 @@ class Ext extends Cls; Cls::s_pub(); // Ok Cls::s_prot(); // Ok endtask + class ExtInner extends Cls::InnerCls; + task check; + if (m_pub != 1) $stop; + if (m_prot != 20) $stop; + f_pub(); // Ok + f_prot(); // Ok + s_pub(); // Ok + s_prot(); // Ok + Cls::InnerCls::s_pub(); // Ok + Cls::InnerCls::s_prot(); // Ok + endtask + endclass endclass module t (/*AUTOARG*/); const Cls mod_c = new; + const Cls::InnerCls imod_c = new; initial begin Cls c; + Cls::InnerCls i; Ext e; + Ext::ExtInner ei; if (c.A != 10) $stop; + if (i.A != 10) $stop; c = new; + i = new; e = new; + ei = new; if (c.m_pub != 1) $stop; + if (i.m_pub != 1) $stop; // if (mod_c.A != 10) $stop; + if (imod_c.A != 10) $stop; // c.check(); + i.check(); e.check(); + ei.check(); // Cls::s_pub(); // Ok + Cls::InnerCls::s_pub(); // Ok c.s_pub(); // Ok + i.s_pub(); // Ok e.s_pub(); // Ok + ei.s_pub(); // Ok // $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_local_nested_bad.out b/test_regress/t/t_class_local_nested_bad.out new file mode 100644 index 000000000..8f4b39314 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.out @@ -0,0 +1,9 @@ +%Error-ENCAPSULATED: t/t_class_local_nested_bad.v:14:22: 'name' is hidden as 'local' within this context (IEEE 1800-2023 8.18) + : ... note: In instance 't' + 14 | name = Node::name; + | ^~~~ + t/t_class_local_nested_bad.v:14:22: ... Location of definition + 9 | static local string name; + | ^~~~ + ... For error description see https://verilator.org/warn/ENCAPSULATED?v=latest +%Error: Exiting due to diff --git a/test_regress/t/t_class_local_nested_bad.py b/test_regress/t/t_class_local_nested_bad.py new file mode 100755 index 000000000..710a094ab --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.py @@ -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('simulator') + +test.compile(fails=test.vlt_all, expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_class_local_nested_bad.v b/test_regress/t/t_class_local_nested_bad.v new file mode 100644 index 000000000..f5e7b1155 --- /dev/null +++ b/test_regress/t/t_class_local_nested_bad.v @@ -0,0 +1,25 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + static local string name; + endclass + + string name; + function new(); + name = Node::name; + endfunction +endclass + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_module.v b/test_regress/t/t_class_module.v index 60f043d9c..483026de2 100644 --- a/test_regress/t/t_class_module.v +++ b/test_regress/t/t_class_module.v @@ -7,8 +7,13 @@ module t (/*AUTOARG*/); class Cls; + class Inner; + int imemberinnera; + int imemberinnerb; + endclass int imembera; int imemberb; + Inner innermemberc; endclass : Cls class Dead; @@ -18,10 +23,16 @@ endclass Cls c; if (c != null) $stop; c = new; + if (c.innermemberc != null) $stop; + c.innermemberc = new; c.imembera = 10; c.imemberb = 20; + c.innermemberc.imemberinnera = 30; + c.innermemberc.imemberinnerb = 40; if (c.imembera != 10) $stop; if (c.imemberb != 20) $stop; + if (c.innermemberc.imemberinnera != 30) $stop; + if (c.innermemberc.imemberinnerb != 40) $stop; $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_nested.py b/test_regress/t/t_class_nested.py new file mode 100755 index 000000000..49d728ea8 --- /dev/null +++ b/test_regress/t/t_class_nested.py @@ -0,0 +1,18 @@ +#!/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('simulator') + +test.compile(verilator_flags2=["--exe --main"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_class_nested.v b/test_regress/t/t_class_nested.v new file mode 100644 index 000000000..4a9e2e74d --- /dev/null +++ b/test_regress/t/t_class_nested.v @@ -0,0 +1,90 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class NodeList; + class Node; + string name; + Node link; + + function new(); + name = "node"; + endfunction + endclass + + Node head; +endclass + +class NodeTree; + class Node; + int id; + Node link; + endclass + + Node root; +endclass + +// Based on IEEE 1800-2017 section 8.23 Nested classes +class Outer; + int outerProp; + local int outerLocalProp; + static int outerStaticProp; + static local int outerLocalStaticProp; + + class Inner; + function void innerMethod(Outer h); + outerStaticProp = 1; + outerLocalStaticProp = 1; + h.outerProp = 1; + h.outerLocalProp = 1; + endfunction + endclass +endclass + + +module t(/*AUTOARG*/); + initial begin + NodeList n = new; + NodeList::Node n1 = new; + NodeList::Node n2 = new; + NodeTree tr = new; + NodeTree::Node t1 = new; + NodeTree::Node t2 = new; + Outer o = new; + Outer::Inner i = new; + + i.innerMethod(o); + + if(o.outerProp != 1) $stop; + if(Outer::outerStaticProp != 1) $stop; + + if (n1.name != "node") $stop; + + n1.name = "n1"; + if (n1.name != "n1") $stop; + + n2.name = "n2"; + if (n2.name != "n2") $stop; + + n.head = n1; + n1.link = n2; + if (n.head.name != "n1") $stop; + if (n.head.link.name != "n2") $stop; + + t1.id = 1; + if (t1.id != 1) $stop; + + t2.id = 2; + if (t2.id != 2) $stop; + + tr.root = t1; + t1.link = t2; + if (tr.root.id != 1) $stop; + if (tr.root.link.id != 2) $stop; + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule diff --git a/test_regress/t/t_class_new.v b/test_regress/t/t_class_new.v index 9d625c5a5..8f4ba17d5 100644 --- a/test_regress/t/t_class_new.v +++ b/test_regress/t/t_class_new.v @@ -11,6 +11,17 @@ class ClsNoArg; imembera = 5; if (other != 6) $stop; endfunction : new + class InnerNoArg; + const int imembera; + function new(); + int other = other_func(); + imembera = 5; + if (other != 6) $stop; + endfunction + function int other_func(); + return 6; + endfunction + endclass function int other_func(); return 6; endfunction @@ -52,6 +63,7 @@ module t (/*AUTOARG*/); ClsArg c2; Cls2Arg c3; Cls2Arg c4; + ClsNoArg::InnerNoArg c5 = new; c1 = new; if (c1.imembera != 5) $stop; @@ -72,6 +84,9 @@ module t (/*AUTOARG*/); if (c4.imembera != 6) $stop; if (c4.imemberb != 9) $stop; + c5 = new; + if (c5.imembera != 5) $stop; + $write("*-* All Finished *-*\n"); $finish; end diff --git a/test_regress/t/t_class_param.v b/test_regress/t/t_class_param.v index f2510ed1d..62ed7508a 100644 --- a/test_regress/t/t_class_param.v +++ b/test_regress/t/t_class_param.v @@ -42,6 +42,9 @@ class Cls #(parameter PBASE = 12); return PBASE; endfunction typedef enum { E_PBASE = PBASE } enum_t; + class ClsInner; + bit [PBASE-1:0] member; + endclass endclass typedef Cls#(8) Cls8_t; @@ -136,6 +139,8 @@ module t (/*AUTOARG*/); Cls c12; Cls #(.PBASE(4)) c4; Cls8_t c8; + Cls#()::ClsInner ci; + Cls#(8)::ClsInner ci8; Wrap #(.P(16)) w16; Wrap2 #(.P(32)) w32; SelfRefClassTypeParam src_logic; @@ -155,6 +160,8 @@ module t (/*AUTOARG*/); c12 = new; c4 = new; c8 = new; + ci = new; + ci8 = new; w16 = new; w32 = new; src_int = new; @@ -195,6 +202,10 @@ module t (/*AUTOARG*/); c4.member = 32'haaaaaaaa; c8.member = 32'haaaaaaaa; // verilator lint_on WIDTH + ci.member = 12'haaa; + ci8.member = 8'hff; + if (ci.member != 12'haaa) $stop; + if (ci8.member != 8'hff) $stop; if (c12.member != 12'haaa) $stop; if (c4.member != 4'ha) $stop; if (c12.get_member() != 12'haaa) $stop; diff --git a/test_regress/t/t_class_param_rewrite.v b/test_regress/t/t_class_param_rewrite.v index 17664ec71..b88019127 100644 --- a/test_regress/t/t_class_param_rewrite.v +++ b/test_regress/t/t_class_param_rewrite.v @@ -14,10 +14,18 @@ module test; if (enum_item.first().name() != "BAR_0") $stop; endfunction + class Inner1; + static function void print(); + E enum_item; + if (enum_item.first().name() != "BAR_0") + $stop; + endfunction + endclass endclass initial begin baz#(bar_e)::print(); + baz#(bar_e)::Inner1::print(); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_static_member.v b/test_regress/t/t_class_static_member.v index 1867b73b0..f6a0be448 100644 --- a/test_regress/t/t_class_static_member.v +++ b/test_regress/t/t_class_static_member.v @@ -8,6 +8,12 @@ `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); class Cls; + class InnerCls; + static function int f_inner_cs_st(); + ++c_st; return c_st; + endfunction + endclass + int c_no = 2; //automatic int c_au = 2; // automatic not a legal keyword here static int c_st = 22; @@ -22,7 +28,6 @@ class Cls; static function int f_cs_st (); ++c_st; return c_st; endfunction - endclass module t (/*AUTOARG*/); @@ -43,6 +48,7 @@ module t (/*AUTOARG*/); v = b.f_c_st(); `checkh(v, 26); // v = Cls::f_cs_st(); `checkh(v, 27); + v = Cls::InnerCls::f_inner_cs_st(); `checkh(v, 28); $write("*-* All Finished *-*\n"); $finish; diff --git a/test_regress/t/t_class_virtual.v b/test_regress/t/t_class_virtual.v index 5b2e9511d..8c0b80920 100644 --- a/test_regress/t/t_class_virtual.v +++ b/test_regress/t/t_class_virtual.v @@ -8,18 +8,33 @@ virtual class VBase; virtual function int hello; return 1; endfunction + virtual class VNested; + virtual function int hello; + return 10; + endfunction + endclass endclass class VA extends VBase; virtual function int hello; return 2; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 20; + endfunction + endclass endclass class VB extends VBase; virtual function int hello; return 3; endfunction + class VNested extends VBase::VNested; + virtual function int hello; + return 30; + endfunction + endclass endclass virtual class uvm_phase; @@ -59,18 +74,27 @@ module t; initial begin VA va = new; VB vb = new; + VA::VNested vna = new; + VB::VNested vnb = new; VBase b; + VBase::VNested bn; uvm_build_phase ph; ExtendsCls ec; if (va.hello() != 2) $stop; if (vb.hello() != 3) $stop; + if (vna.hello() != 20) $stop; + if (vnb.hello() != 30) $stop; b = va; + bn = vna; if (b.hello() != 2) $stop; + if (bn.hello() != 20) $stop; b = vb; + bn = vnb; if (b.hello() != 3) $stop; + if (bn.hello() != 30) $stop; ph = new; if (ph.get1() != 1) $stop; diff --git a/test_regress/t/t_implements_nested.py b/test_regress/t/t_implements_nested.py new file mode 100755 index 000000000..c2d985114 --- /dev/null +++ b/test_regress/t/t_implements_nested.py @@ -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() + +test.passes() diff --git a/test_regress/t/t_implements_nested.v b/test_regress/t/t_implements_nested.v new file mode 100644 index 000000000..87fc8d603 --- /dev/null +++ b/test_regress/t/t_implements_nested.v @@ -0,0 +1,16 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class Cls; + // IEEE 2023 only disallows nested interface inside another interface, not + // class + interface class good_can_nest; + endclass +endclass + +module t (/*AUTOARG*/); + Cls c; +endmodule diff --git a/test_regress/t/t_implements_nested_bad.out b/test_regress/t/t_implements_nested_bad.out index 1121c001f..fab3573e0 100644 --- a/test_regress/t/t_implements_nested_bad.out +++ b/test_regress/t/t_implements_nested_bad.out @@ -1,5 +1,4 @@ -%Error-UNSUPPORTED: t/t_implements_nested_bad.v:8:14: Unsupported: class within class - 8 | interface class bad_cannot_nest; - | ^~~~~ - ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: t/t_implements_nested_bad.v:9:17: Interface class shall not be nested within another interface class. (IEEE 1800-2023 8.26) + 9 | interface class bad_cannot_nest; + | ^~~~~ %Error: Exiting due to diff --git a/test_regress/t/t_implements_nested_bad.v b/test_regress/t/t_implements_nested_bad.v index 119033fa3..be483fb27 100644 --- a/test_regress/t/t_implements_nested_bad.v +++ b/test_regress/t/t_implements_nested_bad.v @@ -5,7 +5,9 @@ // SPDX-License-Identifier: CC0-1.0 class Cls; - interface class bad_cannot_nest; + interface class inte; + interface class bad_cannot_nest; + endclass endclass endclass diff --git a/test_regress/t/t_semaphore_class_nested.py b/test_regress/t/t_semaphore_class_nested.py new file mode 100755 index 000000000..bbbcfc078 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.py @@ -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('simulator') + +test.compile(verilator_flags2=["--binary --timing"]) + +test.passes() diff --git a/test_regress/t/t_semaphore_class_nested.v b/test_regress/t/t_semaphore_class_nested.v new file mode 100644 index 000000000..c18f65b45 --- /dev/null +++ b/test_regress/t/t_semaphore_class_nested.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro. +// SPDX-License-Identifier: CC0-1.0 + +class semaphore_cls; + class InnerKeyClass; + int innerKeys; + function new(int keyCount = 0); + innerKeys = keyCount; + endfunction + endclass + // Test an implementation similar to what Verilator will do internally + InnerKeyClass m_keys; + function new(int keyCount = 0); + m_keys = new(keyCount); + endfunction + function void put(int keyCount = 1); + m_keys.innerKeys += keyCount; + endfunction + task get(int keyCount = 1); + wait (m_keys.innerKeys >= keyCount); + m_keys.innerKeys -= keyCount; + endtask + function int try_get(int keyCount = 1); + if (m_keys.innerKeys >= keyCount) begin + m_keys.innerKeys -= keyCount; + return 1; + end + else begin + return 0; + end + endfunction +endclass + +`define SEMAPHORE_T semaphore_cls + +`include "t_semaphore.v" From 1a07af57a3b264e11c815b2f9a1aa81604f19ea2 Mon Sep 17 00:00:00 2001 From: Yutetsu TAKATSUKASA Date: Tue, 18 Feb 2025 03:34:57 +0900 Subject: [PATCH 76/93] Fix unpacked split_var (#5782) (#5785) --- src/V3SplitVar.cpp | 21 +++++++++++++------ test_regress/t/t_split_var_types.py | 17 ++++++++++++++++ test_regress/t/t_split_var_types.v | 31 +++++++++++++++++++++++++++++ 3 files changed, 63 insertions(+), 6 deletions(-) create mode 100755 test_regress/t/t_split_var_types.py create mode 100644 test_regress/t/t_split_var_types.v diff --git a/src/V3SplitVar.cpp b/src/V3SplitVar.cpp index 26b2cd208..8d29a90ef 100644 --- a/src/V3SplitVar.cpp +++ b/src/V3SplitVar.cpp @@ -900,8 +900,9 @@ public: const AstBasicDType* basicp() const { return m_basicp; } // Make a plan for variables after split // when skipUnused==true, split variable for unread bits will not be created. - std::vector splitPlan(bool skipUnused) const { + std::vector splitPlan(const AstVar* varp, bool skipUnused) const { UASSERT(m_dedupDone, "dedup() must be called before"); + AstNodeDType* const dtypep = varp->dtypeSkipRefp(); std::vector plan; std::vector> points; // points.reserve(m_lhs.size() * 2 + 2); // 2 points will be added per one PackedVarRefEntry @@ -909,9 +910,17 @@ public: points.emplace_back(ref.lsb(), false); // Start of a region points.emplace_back(ref.msb() + 1, true); // End of a region } + int bit_hi, bit_lo; + if (basicp() == dtypep) { + bit_hi = basicp()->hi(); + bit_lo = basicp()->lo(); + } else { // packed struct, packed array. lo is 0 + bit_hi = dtypep->width() - 1; + bit_lo = 0; + } if (skipUnused && !m_rhs.empty()) { // Range to be read must be kept, so add points here - int lsb = m_basicp->hi() + 1; - int msb = m_basicp->lo() - 1; + int lsb = bit_hi + 1; + int msb = bit_lo - 1; for (const PackedVarRefEntry& ref : m_rhs) { lsb = std::min(lsb, ref.lsb()); msb = std::max(msb, ref.msb()); @@ -921,8 +930,8 @@ public: points.emplace_back(msb + 1, true); } if (!skipUnused) { // All bits are necessary - points.emplace_back(m_basicp->lo(), false); - points.emplace_back(m_basicp->hi() + 1, true); + points.emplace_back(bit_lo, false); + points.emplace_back(bit_hi + 1, true); } std::sort(points.begin(), points.end(), SortByFirst()); @@ -1154,7 +1163,7 @@ class SplitPackedVarVisitor final : public VNVisitor, public SplitVarImpl { << " which has " << ref.lhs().size() << " lhs refs and " << ref.rhs().size() << " rhs refs will be split.\n"); std::vector vars - = ref.splitPlan(!varp->isTrace()); // If traced, all bit must be kept + = ref.splitPlan(varp, !varp->isTrace()); // If traced, all bit must be kept if (vars.empty()) continue; if (vars.size() == 1 && vars.front().bitwidth() == varp->width()) continue; // No split diff --git a/test_regress/t/t_split_var_types.py b/test_regress/t/t_split_var_types.py new file mode 100755 index 000000000..6c6dd3e23 --- /dev/null +++ b/test_regress/t/t_split_var_types.py @@ -0,0 +1,17 @@ +#!/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('simulator') + +test.compile(verilator_flags2=['--trace']) + +test.execute() +test.passes() diff --git a/test_regress/t/t_split_var_types.v b/test_regress/t/t_split_var_types.v new file mode 100644 index 000000000..b61923ec0 --- /dev/null +++ b/test_regress/t/t_split_var_types.v @@ -0,0 +1,31 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 +// SPDX-License-Identifier: CC0-1.0 + +module t(/*AUTOARG*/ + // Inputs + clk + ); + input clk; + + // Test loop + always @ (posedge clk) begin + $write("*-* All Finished *-*\n"); + $finish; + end + + bug5782 u_bug5782(.data_out()); + +endmodule + +// #5782 internal error with --trace. Bit range is not properly handled. +module bug5782 ( + output logic [31:0][15:0] data_out +); + logic [31:0][15:0] data [8] /*verilator split_var*/; + always begin + data_out = data[7]; + end +endmodule From 4092ac7e72e316ea1622092b82790a3689f1d3ea Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 18 Feb 2025 01:05:10 -0500 Subject: [PATCH 77/93] Commentary: Changes update --- Changes | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Changes b/Changes index c8d830751..ec5bfc0d8 100644 --- a/Changes +++ b/Changes @@ -18,6 +18,7 @@ Verilator 5.033 devel * Add `--preproc-token-limit` (#5768). [Krzysztof Bieganski, Antmicro Ltd.] * Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton] * Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki] +* Support nested classes (#4178) (#5778). [Kamil Rakoczy, Antmicro Ltd.] * Support VPI array accessors (#5612). [Moubarak Jeje] * Support unpacked structure constrained randomization (#5657) (#5759). [Yilou Wang] * Support generated classes (#5665). [Shou-Li Hsu] @@ -50,6 +51,7 @@ Verilator 5.033 devel * Fix handling forced assigns in V3Life (#5757). [Bartłomiej Chmiel, Antmicro Ltd.] * Fix VFileContent reference count (#5769) (#5771). [Dave Sargeant] * Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.] +* Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA] * Fix matching language extension options including dots. From fe1517164998cc71f81db389a1daf016be01453d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Tue, 18 Feb 2025 01:14:51 -0500 Subject: [PATCH 78/93] Internals: cppcheck cleanups. No functional change. --- include/verilated_random.cpp | 13 +++++++------ include/verilated_vpi.cpp | 4 ---- src/V3AstNodeExpr.h | 4 ++-- src/V3Randomize.cpp | 3 +-- 4 files changed, 10 insertions(+), 14 deletions(-) diff --git a/include/verilated_random.cpp b/include/verilated_random.cpp index 7e048e6c1..30e45d64d 100644 --- a/include/verilated_random.cpp +++ b/include/verilated_random.cpp @@ -481,13 +481,14 @@ bool VlRandomizer::parseSolution(std::iostream& f) { } const std::string indexed_name = oss.str(); - const auto it = std::find_if(m_arr_vars.begin(), m_arr_vars.end(), - [&indexed_name](const auto& entry) { - return entry.second->m_name == indexed_name; - }); - if (it != m_arr_vars.end()) { + const auto iti = std::find_if(m_arr_vars.begin(), m_arr_vars.end(), + [&indexed_name](const auto& entry) { + return entry.second->m_name == indexed_name; + }); + if (iti != m_arr_vars.end()) { std::ostringstream ss; - ss << "#x" << std::hex << std::setw(8) << std::setfill('0') << it->second->m_index; + ss << "#x" << std::hex << std::setw(8) << std::setfill('0') + << iti->second->m_index; idx = ss.str(); } else { VL_FATAL_MT(__FILE__, __LINE__, "randomize", diff --git a/include/verilated_vpi.cpp b/include/verilated_vpi.cpp index ac885332d..7654146a1 100644 --- a/include/verilated_vpi.cpp +++ b/include/verilated_vpi.cpp @@ -2779,7 +2779,6 @@ vpiHandle vpi_put_value(vpiHandle object, p_vpi_value valuep, p_vpi_time /*time_ } return object; } else if (valuep->format == vpiOctStrVal) { - const int chars = (varBits + 2) / 3; const int len = std::strlen(valuep->value.str); for (int i = 0; i < len; ++i) { char digit = valuep->value.str[len - i - 1] - '0'; @@ -3127,7 +3126,6 @@ void vl_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_INT arrayvalue_p->value.shortints = shortintsp; if (varp->vltype() == VLVT_UINT8) { - const CData* ptr = reinterpret_cast(vop->varDatap()); vl_get_value_array_integrals(index, num, size, varp->entBits(), leftIsLow, vop->varCDatap(), shortintsp); } else if (varp->vltype() == VLVT_UINT16) { @@ -3305,8 +3303,6 @@ void vpi_get_value_array(vpiHandle object, p_vpi_arrayvalue arrayvalue_p, PLI_IN return; } - const VerilatedVar* const varp = vop->varp(); - int lowRange = vop->rangep()->low(); int highRange = vop->rangep()->high(); diff --git a/src/V3AstNodeExpr.h b/src/V3AstNodeExpr.h index f3e418a37..5074373aa 100644 --- a/src/V3AstNodeExpr.h +++ b/src/V3AstNodeExpr.h @@ -1668,7 +1668,7 @@ class AstParseHolder final : public AstNodeExpr { // A reference to something soon to replace, used in a select at parse time // that needs conversion to pull the upper lvalue later public: - AstParseHolder(FileLine* fl) + explicit AstParseHolder(FileLine* fl) : ASTGEN_SUPER_ParseHolder(fl) {} ASTGEN_MEMBERS_AstParseHolder; string emitVerilog() override { V3ERROR_NA_RETURN(""); } @@ -4511,7 +4511,7 @@ public: // === AstNodeTermop === class AstInferredDisable final : public AstNodeTermop { public: - AstInferredDisable(FileLine* fl) + explicit AstInferredDisable(FileLine* fl) : ASTGEN_SUPER_InferredDisable(fl) { dtypeSetLogicSized(1, VSigning::UNSIGNED); } diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index d6b8b67ef..909cfe726 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -640,12 +640,11 @@ class ConstraintExprVisitor final : public VNVisitor { = new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE}; varRefp->classOrPackagep(classOrPackagep); methodp->addPinsp(varRefp); - size_t width = varp->width(); AstNodeDType* tmpDtypep = varp->dtypep(); while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType) || VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType)) tmpDtypep = tmpDtypep->subDTypep(); - width = tmpDtypep->width(); + const size_t width = tmpDtypep->width(); methodp->addPinsp( new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, width}); AstNodeExpr* const varnamep From 34ced254c0b1ebcf996dd922266faabd26da1869 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Wed, 19 Feb 2025 16:42:23 -0500 Subject: [PATCH 79/93] Support expression coverage (#5719) --- bin/verilator | 2 + docs/guide/exe_verilator.rst | 12 +- docs/guide/exe_verilator_coverage.rst | 2 +- docs/guide/simulating.rst | 35 ++ src/V3Coverage.cpp | 377 +++++++++++++++++- src/V3Options.cpp | 2 + src/V3Options.h | 10 +- test_regress/t/t_cover_expr.out | 317 +++++++++++++++ test_regress/t/t_cover_expr.py | 34 ++ test_regress/t/t_cover_expr.v | 161 ++++++++ test_regress/t/t_cover_expr_max.out | 445 ++++++++++++++++++++++ test_regress/t/t_cover_expr_max.py | 35 ++ test_regress/t/t_cover_expr_trace.out | 317 +++++++++++++++ test_regress/t/t_cover_expr_trace.py | 35 ++ test_regress/t/t_opt_const_cov.py | 2 +- test_regress/t/t_wrapper_context_top0.out | 3 + test_regress/t/t_wrapper_context_top1.out | 3 + 17 files changed, 1775 insertions(+), 17 deletions(-) create mode 100644 test_regress/t/t_cover_expr.out create mode 100755 test_regress/t/t_cover_expr.py create mode 100644 test_regress/t/t_cover_expr.v create mode 100644 test_regress/t/t_cover_expr_max.out create mode 100755 test_regress/t/t_cover_expr_max.py create mode 100644 test_regress/t/t_cover_expr_trace.out create mode 100755 test_regress/t/t_cover_expr_trace.py diff --git a/bin/verilator b/bin/verilator index e2c3c343a..0285e2df7 100755 --- a/bin/verilator +++ b/bin/verilator @@ -328,6 +328,8 @@ detailed descriptions of these arguments. --compiler-include Include additional header in the precompiled one --converge-limit Tune convergence settle time --coverage Enable all coverage + --coverage-expr Enable expression coverage + --coverage-expr-max Maximum permutations allowed for an expression --coverage-line Enable line coverage --coverage-max-width Maximum array depth for coverage --coverage-toggle Enable toggle coverage diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 61a99b23e..7f031b4cc 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -237,7 +237,17 @@ Summary: .. option:: --coverage Enables all forms of coverage, an alias for :vlopt:`--coverage-line` - :vlopt:`--coverage-toggle` :vlopt:`--coverage-user`. + :vlopt:`--coverage-toggle :vlopt:`--coverage-expr`` :vlopt:`--coverage-user`. + +.. option:: --coverage-expr + + Enables expression coverage analysis. See :ref:`Expression Coverage`. + +.. option:: --coverage-expr-max + + Rarely needed. Specifies the maximum number of permutations able to be + covered for a given expression. Defaults to 32. Increasing may slow + coverage simulations and make analyzing the results unwieldy. .. option:: --coverage-line diff --git a/docs/guide/exe_verilator_coverage.rst b/docs/guide/exe_verilator_coverage.rst index c29f9d329..23caac867 100644 --- a/docs/guide/exe_verilator_coverage.rst +++ b/docs/guide/exe_verilator_coverage.rst @@ -58,7 +58,7 @@ to read multiple inputs. If no data file is specified, by default, Specifies the directory name to which source files with annotated coverage data should be written. -Points are children of each line coverage- branches or toggle points. +Points are children of each line coverage- branches, expressions or toggle points. When point counts are aggregated into a line, the minimum and maximum counts are used to determine the status of the line (complete, partial, failing) The count is equal to the maximum of the points. diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index 1048199b4..bee16e38a 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -270,6 +270,41 @@ A :option:`/*verilator&32;coverage_off*/` signals that do not need toggle analysis, such as RAMs and register files. +.. _Expression Coverage: + +Expression Coverage +------------- + +With :vlopt:`--coverage` or :vlopt:`--coverage-expr`, Verilator will +automatically add coverage analysis at each expression, indicating with +a truth table how every Boolean truth-table possiblity in the expression occurred. + +Multi-bit expressions are ignored, but sub-expressions with are entirely +Boolean are analyzed. Expression coverage does not fully explore the truth +table of an expression, rather is looks at each term's contribution. E.g. +an AND operation will check coverage for TT, XF and FX. + +Some expressions may produce too many cover points. Verilator limits the +maximum number of cover poitns per expression to 32, but this may be +controlled with :vlopt:`--coverage-expr-max`. + +Below is an example showing expression coverage produced from `verilator_coverage` +as applied to the condition expression inside an if statement. Each line +shows the minimum number of terms and their values (e.g. `(t1==0 && t2==1)`) needed +to reach a result for the overall expression (e.g. `=> 1`). Each line also +shows the number of times this combination was hit. Note that individual lines +are not mutually exclusive. + +.. code-block:: + + %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); + -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t + -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t + -000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t + -000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t + -000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t + -000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + .. _Suppressing Coverage: Suppressing Coverage diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index afb674551..2f81cdcf9 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -28,6 +28,9 @@ #include "V3Coverage.h" +#include "V3EmitV.h" + +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -39,6 +42,18 @@ class CoverageVisitor final : public VNVisitor { // TYPES using LinenoSet = std::set; + struct CoverTerm final { + AstNodeExpr* m_exprp; // Expression branch term + bool m_objective; // Term objective + std::string m_emitV; // V3EmitV string for cover point comment + CoverTerm(AstNodeExpr* exprp, bool objective, const string& emitV) + : m_exprp{exprp} + , m_objective{objective} + , m_emitV(emitV) {} + }; + using CoverExpr = std::deque; + using CoverExprs = std::list; + struct ToggleEnt final { const string m_comment; // Comment for coverage dump AstNodeExpr* m_varRefp; // How to get to this element @@ -64,12 +79,20 @@ class CoverageVisitor final : public VNVisitor { return m_on && !m_inModOff && nodep->fileline()->coverageOn() && v3Global.opt.coverageLine(); } + bool exprCoverageOn(const AstNode* nodep) const { + return m_on && !m_inModOff && nodep->fileline()->coverageOn() + && v3Global.opt.coverageExpr(); + } }; + enum Objective : uint8_t { NONE, SEEKING, ABORTED }; + // NODE STATE // Entire netlist: // AstIf::user1() -> bool. True indicates ifelse processed + // AstIf::user2() -> bool. True indicates coverage-generated const VNUser1InUse m_inuser1; + const VNUser2InUse m_inuser2; // STATE - across all visitors int m_nextHandle = 0; @@ -77,6 +100,13 @@ class CoverageVisitor final : public VNVisitor { // STATE - for current visit position (use VL_RESTORER) CheckState m_state; // State save-restored on each new coverage scope/block AstNodeModule* m_modp = nullptr; // Current module to add statement to + AstNode* m_exprStmtsp = nullptr; // Node to add expr coverage to + bool m_then = false; // Whether we're iterating the then or else branch + // when m_exprStmtps is an AstIf + CoverExprs m_exprs; // List of expressions that can reach objective + Objective m_seeking = NONE; // Seeking objective for expression coverage + bool m_objective = false; // Expression objective + bool m_ifCond = false; // Visiting if condition bool m_inToggleOff = false; // In function/task etc string m_beginHier; // AstBegin hier name for user coverage points @@ -160,7 +190,7 @@ class CoverageVisitor final : public VNVisitor { UINFO(9, "line create h" << m_state.m_handle << " " << nodep << endl); } void lineTrack(const AstNode* nodep) { - if (m_state.lineCoverageOn(nodep) + if (m_state.lineCoverageOn(nodep) && !m_ifCond && m_state.m_nodep->fileline()->filenameno() == nodep->fileline()->filenameno()) { for (int lineno = nodep->fileline()->firstLineno(); lineno <= nodep->fileline()->lastLineno(); ++lineno) { @@ -222,13 +252,36 @@ class CoverageVisitor final : public VNVisitor { } void visit(AstNodeProcedure* nodep) override { iterateProcedure(nodep); } - void visit(AstWhile* nodep) override { iterateProcedure(nodep); } + // we can cover expressions in while loops, but the counting goes outside + // the while, see: "minimally-intelligent decision about ... clock domain" + // in the Toggle Coverage docs + void visit(AstWhile* nodep) override { iterateProcedure(nodep, false); } void visit(AstNodeFTask* nodep) override { if (!nodep->dpiImport()) iterateProcedure(nodep); } - void iterateProcedure(AstNode* nodep) { + + void insertProcStatement(AstNode* nodep, AstNode* stmtp) { + if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) { + itemp->addStmtsp(stmtp); + } else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) { + itemp->addStmtsp(stmtp); + } else if (AstWhile* const itemp = VN_CAST(nodep, While)) { + itemp->addStmtsp(stmtp); + } else if (AstIf* const itemp = VN_CAST(nodep, If)) { + if (m_then) { + itemp->addThensp(stmtp); + } else { + itemp->addElsesp(stmtp); + } + } else { + nodep->v3fatalSrc("Bad node type"); + } + } + void iterateProcedure(AstNode* nodep, bool exprProc = true) { VL_RESTORER(m_state); + VL_RESTORER(m_exprStmtsp); VL_RESTORER(m_inToggleOff); + if (exprProc) m_exprStmtsp = nodep; m_inToggleOff = true; createHandle(nodep); iterateChildren(nodep); @@ -237,15 +290,7 @@ class CoverageVisitor final : public VNVisitor { AstNode* const newp = newCoverInc(nodep->fileline(), "", "v_line", "block", linesCov(m_state, nodep), 0, traceNameForLine(nodep, "block")); - if (AstNodeProcedure* const itemp = VN_CAST(nodep, NodeProcedure)) { - itemp->addStmtsp(newp); - } else if (AstNodeFTask* const itemp = VN_CAST(nodep, NodeFTask)) { - itemp->addStmtsp(newp); - } else if (AstWhile* const itemp = VN_CAST(nodep, While)) { - itemp->addStmtsp(newp); - } else { - nodep->v3fatalSrc("Bad node type"); - } + insertProcStatement(nodep, newp); } } @@ -404,6 +449,8 @@ class CoverageVisitor final : public VNVisitor { // VISITORS - LINE COVERAGE // Note not AstNodeIf; other types don't get covered void visit(AstIf* nodep) override { + if (nodep->user2()) return; + UINFO(4, " IF: " << nodep << endl); if (m_state.m_on) { // An else-if. When we iterate the if, use "elsif" marking @@ -422,6 +469,10 @@ class CoverageVisitor final : public VNVisitor { CheckState ifState; CheckState elseState; { + VL_RESTORER(m_exprStmtsp); + VL_RESTORER(m_then); + m_exprStmtsp = nodep; + m_then = true; createHandle(nodep); iterateAndNextNull(nodep->thensp()); lineTrack(nodep); @@ -429,6 +480,10 @@ class CoverageVisitor final : public VNVisitor { } m_state = lastState; { + VL_RESTORER(m_exprStmtsp); + VL_RESTORER(m_then); + m_exprStmtsp = nodep; + m_then = false; createHandle(nodep); iterateAndNextNull(nodep->elsesp()); elseState = m_state; @@ -478,6 +533,9 @@ class CoverageVisitor final : public VNVisitor { } m_state = lastState; } + VL_RESTORER(m_ifCond); + m_ifCond = true; + iterateAndNextNull(nodep->condp()); UINFO(9, " done HANDLE " << m_state.m_handle << " for " << nodep << endl); } void visit(AstCaseItem* nodep) override { @@ -542,6 +600,301 @@ class CoverageVisitor final : public VNVisitor { lineTrack(nodep); } + void abortExprCoverage() { + // is possible to hit max while in NONE, see: exprReduce() + // if that happens we don't want to set ABORTED if it isn't already + // since that will bleed into other expressions + if (m_seeking != NONE) m_seeking = ABORTED; + m_exprs.clear(); + } + + bool checkMaxExprs(size_t additional = 0) { + if (m_seeking != ABORTED + && static_cast(m_exprs.size() + additional) <= v3Global.opt.coverageExprMax()) + return false; + abortExprCoverage(); + return true; + } + + void addExprCoverInc(AstNodeExpr* nodep, int start = 0) { + FileLine* const fl = nodep->fileline(); + int count = start; + for (CoverExpr& expr : m_exprs) { + const string name = "expr_" + std::to_string(count); + string comment = "("; + bool first = true; + AstNodeExpr* condp = nullptr; + for (CoverTerm& term : expr) { + comment += (first ? "" : " && ") + term.m_emitV + + "==" + (term.m_objective ? "1" : "0"); + AstNodeExpr* const clonep = term.m_exprp->cloneTree(true); + AstNodeExpr* const termp = term.m_objective ? clonep : new AstLogNot{fl, clonep}; + if (condp) { + condp = new AstLogAnd{fl, condp, termp}; + } else { + condp = termp; + } + first = false; + } + comment += ") => "; + comment += (m_objective ? '1' : '0'); + AstNode* const newp + = newCoverInc(fl, "", "v_expr", comment, "", 0, traceNameForLine(nodep, name)); + UASSERT_OBJ(condp, nodep, "No terms in expression coverage branch"); + AstIf* const ifp = new AstIf{fl, condp, newp, nullptr}; + ifp->user2(true); + insertProcStatement(m_exprStmtsp, ifp); + ++count; + } + } + + void coverExprs(AstNodeExpr* nodep) { + if (!m_state.exprCoverageOn(nodep) || nodep->dtypep()->width() != 1 || !m_exprStmtsp) { + return; + } + + UASSERT_OBJ(m_seeking == NONE, nodep, "recursively covering expressions is not expected"); + UASSERT_OBJ(m_exprs.empty(), nodep, "unexpected expression coverage garbage"); + VL_RESTORER(m_seeking); + VL_RESTORER(m_objective); + VL_RESTORER(m_exprs); + + m_seeking = SEEKING; + m_objective = false; + iterate(nodep); + CoverExprs falseExprs; + m_exprs.swap(falseExprs); + + m_objective = true; + iterate(nodep); + if (checkMaxExprs(falseExprs.size())) return; + + addExprCoverInc(nodep); + const int start = m_exprs.size(); + m_objective = false; + m_exprs.swap(falseExprs); + addExprCoverInc(nodep, start); + } + + void exprEither(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false, + bool rObjective = false) { + VL_RESTORER(m_objective); + AstNodeExpr* const lhsp = nodep->lhsp(); + AstNodeExpr* const rhsp = nodep->rhsp(); + + if (overrideObjective) m_objective = lObjective; + iterate(lhsp); + if (checkMaxExprs()) return; + CoverExprs lhsExprs; + m_exprs.swap(lhsExprs); + if (overrideObjective) m_objective = rObjective; + iterate(rhsp); + m_exprs.splice(m_exprs.end(), lhsExprs); + checkMaxExprs(); + } + + void exprBoth(AstNodeBiop* nodep, bool overrideObjective = false, bool lObjective = false, + bool rObjective = false) { + VL_RESTORER(m_objective); + AstNodeExpr* const lhsp = nodep->lhsp(); + AstNodeExpr* const rhsp = nodep->rhsp(); + + if (overrideObjective) m_objective = lObjective; + iterate(lhsp); + if (checkMaxExprs()) return; + CoverExprs lhsExprs; + m_exprs.swap(lhsExprs); + if (overrideObjective) m_objective = rObjective; + iterate(rhsp); + if (checkMaxExprs()) return; + CoverExprs rhsExprs; + m_exprs.swap(rhsExprs); + + for (CoverExpr& l : lhsExprs) { + for (CoverExpr& r : rhsExprs) { + // array size 2 -> (false, true) + std::array, 2> varps; + std::array, 2> strs; + + UASSERT_OBJ(!l.empty() && !r.empty(), nodep, "Empty coverage expression branch"); + CoverExpr expr; + + // Compare Vars for simple VarRefs otherwise compare stringified terms + // remove redundant terms and remove entire expression branches when + // terms conflict + // Equivalent terms which don't match on either of these criteria will + // not be flagged as redundant or impossible, however the results will + // still be valid, albeit messier + for (CoverTerm& term : l) { + if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + varps[term.m_objective].insert(refp->varp()); + } else { + strs[term.m_objective].insert(term.m_emitV); + } + expr.push_back(term); + } + bool impossible = false; + for (CoverTerm& term : r) { + bool redundant = false; + if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + if (varps[term.m_objective].find(refp->varp()) + != varps[term.m_objective].end()) + redundant = true; + if (varps[!term.m_objective].find(refp->varp()) + != varps[!term.m_objective].end()) + impossible = true; + } else { + if (strs[term.m_objective].find(term.m_emitV) + != strs[term.m_objective].end()) + redundant = true; + if (strs[!term.m_objective].find(term.m_emitV) + != strs[!term.m_objective].end()) + impossible = true; + } + + if (!redundant) expr.push_back(term); + } + if (!impossible) m_exprs.push_back(std::move(expr)); + if (checkMaxExprs()) return; + } + } + } + + void orExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprEither(nodep); + } else { + exprBoth(nodep); + } + lineTrack(nodep); + } + void visit(AstLogOr* nodep) override { orExpr(nodep); } + void visit(AstOr* nodep) override { orExpr(nodep); } + + void andExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprBoth(nodep); + } else { + exprEither(nodep); + } + lineTrack(nodep); + } + void visit(AstLogAnd* nodep) override { andExpr(nodep); } + void visit(AstAnd* nodep) override { andExpr(nodep); } + + void xorExpr(AstNodeBiop* nodep) { + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + for (const bool lObjective : {false, true}) { + CoverExprs prevExprs; + m_exprs.swap(prevExprs); + const bool rObjective = lObjective ^ m_objective; + exprBoth(nodep, true, lObjective, rObjective); + m_exprs.splice(m_exprs.end(), prevExprs); + if (checkMaxExprs()) break; + } + } + lineTrack(nodep); + } + void visit(AstXor* nodep) override { xorExpr(nodep); } + + void exprNot(AstNodeExpr* nodep) { + VL_RESTORER(m_objective); + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + m_objective = !m_objective; + iterateChildren(nodep); + lineTrack(nodep); + } + } + void visit(AstNot* nodep) override { exprNot(nodep); } + void visit(AstLogNot* nodep) override { exprNot(nodep); } + + template + void exprReduce(AstNodeUniop* nodep) { + if (m_seeking != ABORTED) { + FileLine* const fl = nodep->fileline(); + AstNodeExpr* const lhsp = nodep->lhsp(); + const int width = lhsp->dtypep()->width(); + const size_t expected = std::is_same::value ? 0x1 << width : width + 1; + if (checkMaxExprs(expected)) return; + AstNodeExpr* unrolledp = new AstSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(width - 1)}, + new AstConst{fl, 1}}; + for (int bit = width - 2; bit >= 0; bit--) { + AstSel* const selp = new AstSel{fl, lhsp->cloneTree(true), + new AstConst{fl, static_cast(bit)}, + new AstConst{fl, 1}}; + unrolledp = new T_Oper{fl, selp, unrolledp}; + } + iterate(unrolledp); + pushDeletep(unrolledp); + } else { + iterateChildren(nodep); + lineTrack(nodep); + } + } + void visit(AstRedOr* nodep) override { exprReduce(nodep); } + void visit(AstRedAnd* nodep) override { exprReduce(nodep); } + void visit(AstRedXor* nodep) override { exprReduce(nodep); } + + void visit(AstLogIf* nodep) override { + if (m_seeking == NONE) { + coverExprs(nodep); + } else if (m_objective) { + exprEither(nodep, true, false, true); + } else { + exprBoth(nodep, true, true, false); + } + lineTrack(nodep); + } + + void visit(AstLogEq* nodep) override { + VL_RESTORER(m_objective); + if (m_seeking == NONE) { + coverExprs(nodep); + } else { + m_objective = !m_objective; + xorExpr(nodep); + lineTrack(nodep); + } + } + + void visit(AstCond* nodep) override { + if (m_seeking == NONE) coverExprs(nodep->condp()); + lineTrack(nodep); + } + + // Lambdas not supported for expression coverage + void visit(AstWith* nodep) override { + VL_RESTORER(m_seeking); + if (m_seeking == SEEKING) abortExprCoverage(); + m_seeking = ABORTED; + iterateChildren(nodep); + lineTrack(nodep); + } + + void visit(AstNodeExpr* nodep) override { + if (m_seeking != SEEKING) { + iterateChildren(nodep); + } else { + std::stringstream emitV; + V3EmitV::verilogForTree(nodep, emitV); + // Add new expression with a single term + CoverExpr expr; + expr.emplace_back(nodep, m_objective, emitV.str()); + m_exprs.push_back(std::move(expr)); + checkMaxExprs(); + } + lineTrack(nodep); + } + // VISITORS - BOTH void visit(AstNode* nodep) override { iterateChildren(nodep); diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 1e2024d37..dd0176c84 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1238,6 +1238,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, DECL_OPTION("-compiler-include", CbVal, callStrSetter(&V3Options::addCompilerIncludes)); DECL_OPTION("-converge-limit", Set, &m_convergeLimit); DECL_OPTION("-coverage", CbOnOff, [this](bool flag) { coverage(flag); }); + DECL_OPTION("-coverage-expr", OnOff, &m_coverageExpr); + DECL_OPTION("-coverage-expr-max", Set, &m_coverageExprMax); DECL_OPTION("-coverage-line", OnOff, &m_coverageLine); DECL_OPTION("-coverage-max-width", Set, &m_coverageMaxWidth); DECL_OPTION("-coverage-toggle", OnOff, &m_coverageToggle); diff --git a/src/V3Options.h b/src/V3Options.h index 9ec3918de..2e3c37da8 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -230,6 +230,7 @@ private: bool m_build = false; // main switch: --build bool m_cmake = false; // main switch: --make cmake bool m_context = true; // main switch: --Wcontext + bool m_coverageExpr = false; // main switch: --coverage-expr bool m_coverageLine = false; // main switch: --coverage-block bool m_coverageToggle = false; // main switch: --coverage-toggle bool m_coverageUnderscore = false; // main switch: --coverage-underscore @@ -305,6 +306,7 @@ private: bool m_xmlOnly = false; // main switch: --xml-only int m_buildJobs = -1; // main switch: --build-jobs, -j + int m_coverageExprMax = 32; // main switch: --coverage-expr-max int m_convergeLimit = 100; // main switch: --converge-limit int m_coverageMaxWidth = 256; // main switch: --coverage-max-width int m_expandLimit = 64; // main switch: --expand-limit @@ -426,7 +428,9 @@ private: void addLibExtV(const string& libext); void optimize(int level); void showVersion(bool verbose); - void coverage(bool flag) { m_coverageLine = m_coverageToggle = m_coverageUser = flag; } + void coverage(bool flag) { + m_coverageLine = m_coverageToggle = m_coverageExpr = m_coverageUser = flag; + } static bool suffixed(const string& sw, const char* arg); static string parseFileArg(const string& optdir, const string& relfilename); string filePathCheckOneDir(const string& modname, const string& dirname); @@ -488,8 +492,9 @@ public: bool cmake() const { return m_cmake; } bool context() const VL_MT_SAFE { return m_context; } bool coverage() const VL_MT_SAFE { - return m_coverageLine || m_coverageToggle || m_coverageUser; + return m_coverageLine || m_coverageToggle || m_coverageExpr || m_coverageUser; } + bool coverageExpr() const { return m_coverageExpr; } bool coverageLine() const { return m_coverageLine; } bool coverageToggle() const { return m_coverageToggle; } bool coverageUnderscore() const { return m_coverageUnderscore; } @@ -565,6 +570,7 @@ public: int buildJobs() const VL_MT_SAFE { return m_buildJobs; } int convergeLimit() const { return m_convergeLimit; } + int coverageExprMax() const { return m_coverageExprMax; } int coverageMaxWidth() const { return m_coverageMaxWidth; } bool dumpTreeAddrids() const VL_MT_SAFE; int expandLimit() const { return m_expandLimit; } diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out new file mode 100644 index 000000000..18a70c05d --- /dev/null +++ b/test_regress/t/t_cover_expr.out @@ -0,0 +1,317 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr.py b/test_regress/t/t_cover_expr.py new file mode 100755 index 000000000..0f9b3aca8 --- /dev/null +++ b/test_regress/t/t_cover_expr.py @@ -0,0 +1,34 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') + +test.compile(verilator_flags2=['--cc', '--coverage-expr']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v new file mode 100644 index 000000000..df6a93113 --- /dev/null +++ b/test_regress/t/t_cover_expr.v @@ -0,0 +1,161 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + return ~x; + endfunction + + function automatic bit and_oper(bit a, bit b); + return a & b; + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; + if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); + if ((~t1 && t2) || (~t3 && t4)) $write(""); + if (t3 && (t1 == t2)) $write(""); + if (123 == (124 - 32'(t1 || t2))) $write(""); + some_int <= (t2 || t3) ? 345 : 567; + some_bool <= t1 && t2; + if (t1 & t2) $write(""); + if ((!t1 && t2) | (~t3 && t4)) $write(""); + if (t1 ^ t2) $write(""); + if (~(t1 & t2)) $write(""); + if (t1 -> t2) $write(""); + if (t1 <-> t2) $write(""); + if (&cyc[2:0]) $write(""); + if (&cyc[3:2]) $write(""); + if (|cyc[2:0]) $write(""); + if (^cyc[2:0]) $write(""); + if (|cyc[2:0] || cyc[3]) $write(""); + if (t1 & t2 & 1'b1) $write(""); + if (t1 & t2 & 1'b0) $write(""); + if (t1 & t2 & ONE) $write(""); + if (t1 & t2 & ZERO) $write(""); + if (t1 && t2) begin + $write(""); + end else if (t1 || t2) begin + $write(""); + end + if (invert(t1) && t2) $write(""); + if (and_oper(t1, t2)) $write(""); + if (t2 && t3) begin + if (t1 && t2) $write(""); + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin + if (cyc[loop_var] && t2) $write(""); + end + end + // stop at the first layer even if there's more to find + if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); + // impossible branches and redundant terms + if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); + if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); + // demonstrate current limitations of term matching scheme + if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) + || + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 + ] & + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation + other_int <= t1 ? t2 ? 1 : 2 : 3; + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin + if (t1 && t2) $write(""); + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; + while (ta || tb || tc) begin + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + +endmodule + +module sub ( + input p, + input q +); + + always_comb begin + if (p && q) $write(""); + end + +endmodule diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out new file mode 100644 index 000000000..b755a4ad3 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.out @@ -0,0 +1,445 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large +%000001 if (^cyc[6:0]) $write(""); +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==0 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==0 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==0 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000001 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==0 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==0 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==0) => 1 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==0 && cyc[6:0][6]==1) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==0) => 0 hier=top.t +-000000 point: comment=(cyc[6:0][0]==1 && cyc[6:0][1]==1 && cyc[6:0][2]==1 && cyc[6:0][3]==1 && cyc[6:0][4]==1 && cyc[6:0][5]==1 && cyc[6:0][6]==1) => 1 hier=top.t + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr_max.py b/test_regress/t/t_cover_expr_max.py new file mode 100755 index 000000000..bc3049c66 --- /dev/null +++ b/test_regress/t/t_cover_expr_max.py @@ -0,0 +1,35 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--coverage-expr-max', '128']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out new file mode 100644 index 000000000..18a70c05d --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.out @@ -0,0 +1,317 @@ +// // verilator_coverage annotation + // DESCRIPTION: Verilator: Verilog Test module + // + // This file ONLY is placed under the Creative Commons Public Domain, for + // any use, without warranty, 2024 by Wilson Snyder. + // SPDX-License-Identifier: CC0-1.0 + + module t (/*AUTOARG*/ + // Inputs + clk + ); + + input clk; + + integer cyc; + initial cyc=1; + + integer some_int; + integer other_int; + logic some_bool; + + wire t1 = cyc[0]; + wire t2 = cyc[1]; + wire t3 = cyc[2]; + wire t4 = cyc[3]; + + localparam bit ONE = 1'b1; + localparam bit ZERO = 1'b0; + + function automatic bit invert(bit x); + 000015 return ~x; ++000012 point: comment=(x==0) => 1 hier=top.t ++000015 point: comment=(x==1) => 0 hier=top.t + endfunction + + function automatic bit and_oper(bit a, bit b); +%000005 return a & b; +-000004 point: comment=(a==0) => 0 hier=top.t +-000002 point: comment=(a==1 && b==1) => 1 hier=top.t +-000005 point: comment=(b==0) => 0 hier=top.t + endfunction + + always @ (posedge clk) begin + cyc <= cyc + 1; +%000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); +-000002 point: comment=(cyc[0]==0 && cyc[1]==1) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000004 point: comment=(cyc[0]==1 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t +-000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000005 if (t3 && (t1 == t2)) $write(""); +-000005 point: comment=((t1 == t2)==0) => 0 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +-000002 point: comment=(t3==1 && (t1 == t2)==1) => 1 hier=top.t +%000005 if (123 == (124 - 32'(t1 || t2))) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000004 some_int <= (t2 || t3) ? 345 : 567; +-000003 point: comment=(t2==0 && t3==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +-000004 point: comment=(t3==1) => 1 hier=top.t +%000005 some_bool <= t1 && t2; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2) $write(""); +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000004 if ((!t1 && t2) | (~t3 && t4)) $write(""); +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t +%000003 if (t1 ^ t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +%000005 if (~(t1 & t2)) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 1 hier=top.t +%000004 if (t1 -> t2) $write(""); +-000004 point: comment=(t1==0) => 1 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000004 point: comment=(t2==1) => 1 hier=top.t +%000003 if (t1 <-> t2) $write(""); +-000002 point: comment=(t1==0 && t2==0) => 1 hier=top.t +-000002 point: comment=(t1==0 && t2==1) => 0 hier=top.t +-000003 point: comment=(t1==1 && t2==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +%000005 if (&cyc[2:0]) $write(""); +-000004 point: comment=(cyc[2:0][0]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +-000005 point: comment=(cyc[2:0][1]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][2]==0) => 0 hier=top.t +%000007 if (&cyc[3:2]) $write(""); +-000005 point: comment=(cyc[3:2][0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[3:2][0]==1 && cyc[3:2][1]==1) => 1 hier=top.t +-000007 point: comment=(cyc[3:2][1]==0) => 0 hier=top.t +%000005 if (|cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +%000002 if (^cyc[2:0]) $write(""); +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 0 hier=top.t +-000002 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==0) => 1 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==0 && cyc[2:0][2]==1) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==0) => 0 hier=top.t +-000001 point: comment=(cyc[2:0][0]==1 && cyc[2:0][1]==1 && cyc[2:0][2]==1) => 1 hier=top.t +%000005 if (|cyc[2:0] || cyc[3]) $write(""); +-000000 point: comment=(cyc[2:0][0]==0 && cyc[2:0][1]==0 && cyc[2:0][2]==0 && cyc[3]==0) => 0 hier=top.t +-000005 point: comment=(cyc[2:0][0]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][1]==1) => 1 hier=top.t +-000004 point: comment=(cyc[2:0][2]==1) => 1 hier=top.t +-000002 point: comment=(cyc[3]==1) => 1 hier=top.t +%000005 if (t1 & t2 & 1'b1) $write(""); +-000000 point: comment=(1'h1==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && 1'h1==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & 1'b0) $write(""); +-000009 point: comment=(1'h0==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && 1'h0==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 & t2 & ONE) $write(""); +-000000 point: comment=(ONE==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1 && ONE==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000009 if (t1 & t2 & ZERO) $write(""); +-000009 point: comment=(ZERO==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && ZERO==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if (t1 && t2) begin +-000004 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + $write(""); +%000003 end else if (t1 || t2) begin +-000002 point: comment=(t1==0 && t2==0) => 0 hier=top.t +-000003 point: comment=(t1==1) => 1 hier=top.t +-000002 point: comment=(t2==1) => 1 hier=top.t + $write(""); + end +%000005 if (invert(t1) && t2) $write(""); +-000005 point: comment=(invert(t1)==0) => 0 hier=top.t +-000002 point: comment=(invert(t1)==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + if (and_oper(t1, t2)) $write(""); +%000005 if (t2 && t3) begin +-000005 point: comment=(t2==0) => 0 hier=top.t +-000002 point: comment=(t2==1 && t3==1) => 1 hier=top.t +-000005 point: comment=(t3==0) => 0 hier=top.t +%000001 if (t1 && t2) $write(""); +-000001 point: comment=(t1==0) => 0 hier=top.t +-000001 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + if (0 == 1) begin + for (int loop_var = 0; loop_var < 1; loop_var++) begin +%000000 if (cyc[loop_var] && t2) $write(""); +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==0) => 0 hier=top.t +-000000 point: comment=(cyc[loop_var[4:0]+:32'h1]]==1 && t2==1) => 1 hier=top.t +-000000 point: comment=(t2==0) => 0 hier=top.t + end + end + // stop at the first layer even if there's more to find +%000007 if ((cyc[3+32'(t1 && t2)+:2] == cyc[5+32'(t3 || t4)+:2]) || cyc[31]) $write(""); +-000002 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==0 && cyc[31]==0) => 0 hier=top.t +-000007 point: comment=((cyc[(32'sh3 + (t1 && t2))[4:0]+:32'sh2]] == cyc[(32'sh5 + (t3 || t4))[4:0]+:32'sh2]])==1) => 1 hier=top.t +-000000 point: comment=(cyc[31]==1) => 1 hier=top.t + // impossible branches and redundant terms +%000005 if ((t1 && t2) && ~(t1 && t3) && (t1 || t4)) $write(""); +-000003 point: comment=(t1==0 && t4==0) => 0 hier=top.t +-000004 point: comment=(t1==0) => 0 hier=top.t +-000000 point: comment=(t1==1 && t2==1 && t3==0 && t4==1) => 1 hier=top.t +-000001 point: comment=(t1==1 && t2==1 && t3==0) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[0] && cyc[2]) && (cyc[0] || cyc[3])) $write(""); +-000003 point: comment=(cyc[0]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0) => 1 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[2]==1) => 0 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + // demonstrate current limitations of term matching scheme +%000005 if ((cyc[0] && cyc[1]) && ~(cyc[1-1] && cyc[2]) && (cyc[2-2] || cyc[3])) $write(""); +-000002 point: comment=(cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==1 && cyc[2]==1) => 0 hier=top.t +-000003 point: comment=(cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==0 && cyc[3]==0) => 0 hier=top.t +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[(32'sh1 - 32'sh1)[4:0]+:32'h1]]==0 && cyc[3]==1) => 1 hier=top.t +-000001 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[(32'sh2 - 32'sh2)[4:0]+:32'h1]]==1) => 1 hier=top.t +-000000 point: comment=(cyc[0]==1 && cyc[1]==1 && cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + //verilator coverage_off + if (t1 && t2) $write(""); + //verilator coverage_on + if ((~t1 && t2) +%000004 || +-000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t +-000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t +-000004 point: comment=(t1==1 && t4==0) => 0 hier=top.t +-000002 point: comment=(t2==0 && t3==1) => 0 hier=top.t +-000003 point: comment=(t2==0 && t4==0) => 0 hier=top.t +-000002 point: comment=(t3==0 && t4==1) => 1 hier=top.t + (~t3 && t4)) $write(""); + // intentionally testing wonkified expression terms + if ( + cyc[ + 0 +%000005 ] & +-000004 point: comment=(cyc[0]==0) => 0 hier=top.t +-000002 point: comment=(cyc[0]==1 && cyc[1]==1) => 1 hier=top.t +-000005 point: comment=(cyc[1]==0) => 0 hier=top.t + cyc + [1]) $write(""); + // for now each ternary condition is considered in isolation +%000005 other_int <= t1 ? t2 ? 1 : 2 : 3; +-000004 point: comment=(t1==0) => 0 hier=top.t +-000005 point: comment=(t1==1) => 1 hier=top.t + // no expression coverage for multi-bit expressions + if ((cyc[1:0] & cyc[3:2]) == 2'b11) $write(""); + // truth table is too large + if (^cyc[6:0]) $write(""); + // this one is too big even for t_cover_expr_max + if (^cyc) $write(""); + if (cyc==9) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end + + always_comb begin +%000005 if (t1 && t2) $write(""); +-000005 point: comment=(t1==0) => 0 hier=top.t +-000002 point: comment=(t1==1 && t2==1) => 1 hier=top.t +-000005 point: comment=(t2==0) => 0 hier=top.t + end + + logic ta, tb, tc; + initial begin + int q[5]; + int qv[$]; + + q = '{1, 2, 2, 4, 3}; + // lambas not handled + // NB: there is a bug w/ tracing find_first (maybe lambdas in general?) + // tracing_off does not work around the bug + qv = q.find_first with (item[0] & item[1]); + ta = '1; + tb = '0; + tc = '0; +%000001 while (ta || tb || tc) begin +-000001 point: comment=(ta==0 && tb==0 && tc==0) => 0 hier=top.t +-000000 point: comment=(ta==1) => 1 hier=top.t +-000000 point: comment=(tb==1) => 1 hier=top.t +-000000 point: comment=(tc==1) => 1 hier=top.t + tc = tb; + tb = ta; + ta = '0; + end + end + + sub the_sub_1 (.p(t1), .q(t2)); + sub the_sub_2 (.p(t3), .q(t4)); + // TODO -- non-process expressions + sub the_sub_3 (.p(t1 ? t2 : t3), .q(t4)); + + // TODO + // pragma for expr coverage off / on + // investigate cover point sorting in annotated source + // consider reporting don't care terms + // + // Branches which are statically impossible to reach are still reported. + // E.g. + // -000000 point: comment=(t1=1 && t2=1 && 1'h0=1) => 1 hier=top.t + // These could potentially be pruned, but they currently follow suit for + // what branch coverage does. Perhaps a switch should be added to not + // count statically impossible things. + + endmodule + + module sub ( + input p, + input q + ); + + always_comb begin +~000019 if (p && q) $write(""); ++000017 point: comment=(p==0) => 0 hier=top.t.the_sub_* +-000002 point: comment=(p==1 && q==1) => 1 hier=top.t.the_sub_* ++000019 point: comment=(q==0) => 0 hier=top.t.the_sub_* + end + + endmodule + diff --git a/test_regress/t/t_cover_expr_trace.py b/test_regress/t/t_cover_expr_trace.py new file mode 100755 index 000000000..c1fd9df23 --- /dev/null +++ b/test_regress/t/t_cover_expr_trace.py @@ -0,0 +1,35 @@ +#!/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 +from pathlib import Path + +test.scenarios('simulator') +test.top_filename = "t/t_cover_expr.v" + +test.compile(verilator_flags2=['--cc', '--coverage-expr', '--trace']) + +test.execute() + +# Read the input .v file and do any CHECK_COVER requests +test.inline_checks() + +test.run(cmd=[ + os.environ["VERILATOR_ROOT"] + "/bin/verilator_coverage", + "--annotate-points", + "--annotate", + test.obj_dir + "/annotated", + test.obj_dir + "/coverage.dat", +], + verilator_run=True) + +top = Path(test.top_filename) +test.files_identical(test.obj_dir + f"/annotated/{top.name}", test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_opt_const_cov.py b/test_regress/t/t_opt_const_cov.py index 1af2400b7..147adc0df 100755 --- a/test_regress/t/t_opt_const_cov.py +++ b/test_regress/t/t_opt_const_cov.py @@ -16,6 +16,6 @@ test.compile(verilator_flags2=["-Wno-UNOPTTHREADS", "--stats", "--coverage", "-- test.execute() if test.vlt: - test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 144) + test.file_grep(test.stats, r'Optimizations, Const bit op reduction\s+(\d+)', 620) test.passes() diff --git a/test_regress/t/t_wrapper_context_top0.out b/test_regress/t/t_wrapper_context_top0.out index d028fb387..74af3f696 100644 --- a/test_regress/t/t_wrapper_context_top0.out +++ b/test_regress/t/t_wrapper_context_top0.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop0.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop0.top' 0 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop0.top' 34 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop0.top' 0 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop0.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop0.top' 1 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop0.top' 33 diff --git a/test_regress/t/t_wrapper_context_top1.out b/test_regress/t/t_wrapper_context_top1.out index 391e5cb35..f7fab4025 100644 --- a/test_regress/t/t_wrapper_context_top1.out +++ b/test_regress/t/t_wrapper_context_top1.out @@ -75,5 +75,8 @@ C 'ft/t_wrapper_context.vl38n4pagev_line/topoblockS38-39htop1.top' C 'ft/t_wrapper_context.vl40n7pagev_branch/topoifS40htop1.top' 19 C 'ft/t_wrapper_context.vl40n8pagev_branch/topoelseS46htop1.top' 0 C 'ft/t_wrapper_context.vl41n11pagev_line/topoelsehtop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==0) => 0htop1.top' 18 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo((counter >= 32'sh5)==1 && stop==1) => 1htop1.top' 1 +C 'ft/t_wrapper_context.vl41n27pagev_expr/topo(stop==0) => 0htop1.top' 0 C 'ft/t_wrapper_context.vl47n10pagev_branch/topoifS47-49htop1.top' 0 C 'ft/t_wrapper_context.vl47n11pagev_branch/topoelsehtop1.top' 0 From 2d0149b703ef33cdb497f7aa2d2e102c418b9854 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 19 Feb 2025 17:00:29 -0500 Subject: [PATCH 80/93] Commentary: Changes update --- Changes | 4 ++++ docs/guide/exe_verilator.rst | 2 +- docs/guide/simulating.rst | 9 +++++---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/Changes b/Changes index ec5bfc0d8..9a9833d2f 100644 --- a/Changes +++ b/Changes @@ -11,6 +11,10 @@ contributors that suggested a given feature are shown in []. Thanks! Verilator 5.033 devel ========================== +**Major:** + +* Add expression coverage (#5719). [Todd Strader] + **Minor:** * Add `COVERIGN` warning, as a more specific UNSUPPORTED error. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 7f031b4cc..155e93ce1 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -237,7 +237,7 @@ Summary: .. option:: --coverage Enables all forms of coverage, an alias for :vlopt:`--coverage-line` - :vlopt:`--coverage-toggle :vlopt:`--coverage-expr`` :vlopt:`--coverage-user`. + :vlopt:`--coverage-toggle` :vlopt:`--coverage-expr` :vlopt:`--coverage-user`. .. option:: --coverage-expr diff --git a/docs/guide/simulating.rst b/docs/guide/simulating.rst index bee16e38a..49ba5c720 100644 --- a/docs/guide/simulating.rst +++ b/docs/guide/simulating.rst @@ -273,11 +273,12 @@ signals that do not need toggle analysis, such as RAMs and register files. .. _Expression Coverage: Expression Coverage -------------- +------------------- With :vlopt:`--coverage` or :vlopt:`--coverage-expr`, Verilator will -automatically add coverage analysis at each expression, indicating with -a truth table how every Boolean truth-table possiblity in the expression occurred. +automatically add coverage analysis at each expression, indicating with a +truth table how every Boolean truth-table possibility in the expression +occurred. Multi-bit expressions are ignored, but sub-expressions with are entirely Boolean are analyzed. Expression coverage does not fully explore the truth @@ -285,7 +286,7 @@ table of an expression, rather is looks at each term's contribution. E.g. an AND operation will check coverage for TT, XF and FX. Some expressions may produce too many cover points. Verilator limits the -maximum number of cover poitns per expression to 32, but this may be +maximum number of cover points per expression to 32, but this may be controlled with :vlopt:`--coverage-expr-max`. Below is an example showing expression coverage produced from `verilator_coverage` From be1ace423e9179915ad86f692bdb58b58cd35239 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 19 Feb 2025 17:03:24 -0500 Subject: [PATCH 81/93] Fix time import error on time parameters (#5786). --- Changes | 3 ++- src/V3LinkParse.cpp | 1 + test_regress/t/t_time_param.py | 18 ++++++++++++++++ test_regress/t/t_time_param.v | 39 ++++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_time_param.py create mode 100644 test_regress/t/t_time_param.v diff --git a/Changes b/Changes index 9a9833d2f..a13ab3d18 100644 --- a/Changes +++ b/Changes @@ -13,7 +13,7 @@ Verilator 5.033 devel **Major:** -* Add expression coverage (#5719). [Todd Strader] +* Add expression coverage (#4677) (#5719). [Todd Strader] **Minor:** @@ -56,6 +56,7 @@ Verilator 5.033 devel * Fix VFileContent reference count (#5769) (#5771). [Dave Sargeant] * Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.] * Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA] +* Fix time import error on time parameters (#5786). [Luca Colagrande] * Fix matching language extension options including dots. diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 782ea6c12..26017f4e4 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -718,6 +718,7 @@ class LinkParseVisitor final : public VNVisitor { nodep->name(newName); nodep->origName(newName); } + iterateChildren(nodep); } void visit(AstGenCase* nodep) override { ++m_genblkNum; diff --git a/test_regress/t/t_time_param.py b/test_regress/t/t_time_param.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_time_param.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_time_param.v b/test_regress/t/t_time_param.v new file mode 100644 index 000000000..52abb1de3 --- /dev/null +++ b/test_regress/t/t_time_param.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps + +module vip_snitch_cluster + #(parameter realtime ClkPeriod = 10ns) + (output logic clk_o); + + initial begin + forever begin + clk_o = 1; + #(ClkPeriod/2); + clk_o = 0; + #(ClkPeriod/2); + end + end + + initial begin + #(ClkPeriod*100); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule + +module t; + logic clk; + + vip_snitch_cluster #( + .ClkPeriod(1ns) + ) vip ( + .clk_o(clk) + ); + +endmodule From 2a23ec74e466935fdb800f77f31f5bdd3f474fd1 Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Thu, 20 Feb 2025 10:15:09 +0100 Subject: [PATCH 82/93] Fix dpi context functions (#5788) --- src/V3LinkResolve.cpp | 3 ++- test_regress/t/t_dpi_context.py | 2 +- test_regress/t/t_dpi_context.v | 7 +++++++ test_regress/t/t_dpi_context_c.cpp | 3 +++ 4 files changed, 13 insertions(+), 2 deletions(-) diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index cb268fa1e..9d5630141 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -221,7 +221,8 @@ class LinkResolveVisitor final : public VNVisitor { letp->user2(false); return; } - if (nodep->taskp() && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { + if (nodep->taskp() && !nodep->scopeNamep() + && (nodep->taskp()->dpiContext() || nodep->taskp()->dpiExport())) { nodep->scopeNamep(new AstScopeName{nodep->fileline(), false}); } } diff --git a/test_regress/t/t_dpi_context.py b/test_regress/t/t_dpi_context.py index 8f4de8825..bcdb2941d 100755 --- a/test_regress/t/t_dpi_context.py +++ b/test_regress/t/t_dpi_context.py @@ -11,7 +11,7 @@ import vltest_bootstrap test.scenarios('simulator') -test.compile(v_flags2=["t/t_dpi_context_c.cpp"]) +test.compile(v_flags2=["t/t_dpi_context_c.cpp", "--debug", "-no-dump-tree"]) test.execute() diff --git a/test_regress/t/t_dpi_context.v b/test_regress/t/t_dpi_context.v index b0e11a23f..d246db028 100644 --- a/test_regress/t/t_dpi_context.v +++ b/test_regress/t/t_dpi_context.v @@ -32,6 +32,7 @@ module sub (input integer inst); import "DPI-C" context function int dpic_save(int value); import "DPI-C" context function int dpic_restore(); import "DPI-C" context function int unsigned dpic_getcontext(); + import "DPI-C" context function int unsigned dpic_get1(); int result; @@ -63,6 +64,11 @@ module sub (input integer inst); if (dpic_restore() != 23+inst) $stop; endtask + function automatic int call_dpic_get1; + int res = dpic_get1(); + return res; + endfunction + int unsigned cntxt1; int unsigned cntxt2; @@ -74,6 +80,7 @@ module sub (input integer inst); end // svContext should be the context of the function declaration, not the context of the function call if (cntxt1 != cntxt2) $stop; + if (call_dpic_get1() != 1) $stop; end endmodule diff --git a/test_regress/t/t_dpi_context_c.cpp b/test_regress/t/t_dpi_context_c.cpp index 9025e9407..9d27e8fd8 100644 --- a/test_regress/t/t_dpi_context_c.cpp +++ b/test_regress/t/t_dpi_context_c.cpp @@ -42,6 +42,7 @@ extern int dpic_line(); extern int dpic_save(int value); extern int dpic_restore(); extern unsigned dpic_getcontext(); +extern unsigned dpic_get1(); } #endif @@ -151,6 +152,8 @@ unsigned dpic_getcontext() { return (unsigned)(uintptr_t)scope; } +unsigned dpic_get1() { return 1; } + void dpic_final() { static int s_once = 0; if (s_once++) return; From 70459b32086e0045878b381aee0a915b3984bde6 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Thu, 20 Feb 2025 13:49:32 -0500 Subject: [PATCH 83/93] Fix V3EmitV for non-zero based arrays (#5790) --- src/V3EmitV.cpp | 20 +++++++++++++------- test_regress/t/t_cover_expr.out | 10 ++++++++++ test_regress/t/t_cover_expr.v | 4 ++++ test_regress/t/t_cover_expr_max.out | 10 ++++++++++ test_regress/t/t_cover_expr_trace.out | 10 ++++++++++ test_regress/t/t_debug_emitv.out | 25 +++++++++++++++++++++++++ test_regress/t/t_debug_emitv.v | 11 +++++++++++ 7 files changed, 83 insertions(+), 7 deletions(-) diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index 0cc600fab..bf4f6aba5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -602,22 +602,28 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } void visit(AstSel* nodep) override { iterateAndNextConstNull(nodep->fromp()); + int offset = 0; + AstNodeDType* const dtypep = nodep->fromp()->dtypep(); + if (VN_IS(dtypep, BasicDType)) { + AstBasicDType* const basicDtypep = VN_AS(dtypep, BasicDType); + offset = basicDtypep->lo(); + } puts("["); if (VN_IS(nodep->lsbp(), Const)) { if (nodep->widthp()->isOne()) { - if (VN_IS(nodep->lsbp(), Const)) { - puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt())); - } else { - iterateAndNextConstNull(nodep->lsbp()); - } + puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset)); } else { puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() - + VN_AS(nodep->widthp(), Const)->toSInt() - 1)); + + VN_AS(nodep->widthp(), Const)->toSInt() + offset - 1)); puts(":"); - puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt())); + puts(cvtToStr(VN_AS(nodep->lsbp(), Const)->toSInt() + offset)); } } else { iterateAndNextConstNull(nodep->lsbp()); + if (offset != 0) { + puts(" + "); + puts(cvtToStr(offset)); + } putfs(nodep, "+:"); iterateAndNextConstNull(nodep->widthp()); puts("]"); diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out index 18a70c05d..aaa7ae8bd 100644 --- a/test_regress/t/t_cover_expr.out +++ b/test_regress/t/t_cover_expr.out @@ -15,6 +15,9 @@ integer cyc; initial cyc=1; + logic [63:32] cyc2; + always_comb cyc2 = cyc; + integer some_int; integer other_int; logic some_bool; @@ -49,6 +52,13 @@ -000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t -000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t -000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v index df6a93113..4b120982b 100644 --- a/test_regress/t/t_cover_expr.v +++ b/test_regress/t/t_cover_expr.v @@ -14,6 +14,9 @@ module t (/*AUTOARG*/ integer cyc; initial cyc=1; + logic [63:32] cyc2; + always_comb cyc2 = cyc; + integer some_int; integer other_int; logic some_bool; @@ -37,6 +40,7 @@ module t (/*AUTOARG*/ always @ (posedge clk) begin cyc <= cyc + 1; if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); + if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); if ((~t1 && t2) || (~t3 && t4)) $write(""); if (t3 && (t1 == t2)) $write(""); if (123 == (124 - 32'(t1 || t2))) $write(""); diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out index b755a4ad3..4846df8cb 100644 --- a/test_regress/t/t_cover_expr_max.out +++ b/test_regress/t/t_cover_expr_max.out @@ -15,6 +15,9 @@ integer cyc; initial cyc=1; + logic [63:32] cyc2; + always_comb cyc2 = cyc; + integer some_int; integer other_int; logic some_bool; @@ -49,6 +52,13 @@ -000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t -000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t -000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out index 18a70c05d..aaa7ae8bd 100644 --- a/test_regress/t/t_cover_expr_trace.out +++ b/test_regress/t/t_cover_expr_trace.out @@ -15,6 +15,9 @@ integer cyc; initial cyc=1; + logic [63:32] cyc2; + always_comb cyc2 = cyc; + integer some_int; integer other_int; logic some_bool; @@ -49,6 +52,13 @@ -000002 point: comment=(cyc[1]==0 && cyc[2]==1) => 0 hier=top.t -000003 point: comment=(cyc[1]==0 && cyc[3]==0) => 0 hier=top.t -000002 point: comment=(cyc[2]==0 && cyc[3]==1) => 1 hier=top.t +%000004 if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); +-000002 point: comment=(cyc2[32]==0 && cyc2[33]==1) => 1 hier=top.t +-000002 point: comment=(cyc2[32]==1 && cyc2[34]==1) => 0 hier=top.t +-000004 point: comment=(cyc2[32]==1 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t +-000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t +-000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index efa089909..77d14de96 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -28,6 +28,15 @@ module Vt_debug_emitv_t; initial begin array = '{0:32'sh1, 1:32'sh2, 2:32'sh3}; end + logic [63:32] downto_32; + downto_32 = 32'h0; + function ident; + input int signed value; + begin : label0 + ident = value; + disable label0; + end + endfunction initial begin begin if ($test$plusargs(40'h48454c4c4f)) begin @@ -45,6 +54,22 @@ module Vt_debug_emitv_t; else begin $display("+TEST= not found"); end + if (downto_32[33]) begin + $write(""); + end + if (downto_32[(ident('sh21) - 'h20)[5:0] + 32 + +:'h1]]) begin + $write(""); + end + if ((| downto_32[48:40])) begin + $write(""); + end + if ((| downto_32[57:55])) begin + $write(""); + end + if ((| downto_32[60:54])) begin + $write(""); + end end end bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index c6cad71ea..c6b1d91b3 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -61,6 +61,12 @@ module t (/*AUTOARG*/ integer i1; int array[3]; initial array = '{1,2,3}; + logic [63:32] downto_32 = '0; + + function automatic int ident(int value); + return value; + endfunction + initial begin if ($test$plusargs("HELLO")) $display("Hello argument found."); if (Pkg::FOO == 0) $write(""); @@ -69,6 +75,11 @@ module t (/*AUTOARG*/ $display("value was %d", i1); else $display("+TEST= not found"); + if (downto_32[33]) $write(""); + if (downto_32[ident(33)]) $write(""); + if (|downto_32[48:40]) $write(""); + if (|downto_32[55+:3]) $write(""); + if (|downto_32[60-:7]) $write(""); end bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; From 2e1fa8f33844c04e0e1d8ff09216e9629b9259c7 Mon Sep 17 00:00:00 2001 From: Kamil Rakoczy Date: Fri, 21 Feb 2025 19:47:54 +0100 Subject: [PATCH 84/93] Add `--preproc-resolve` for modules in preprocessor output (#5789) Signed-off-by: Kamil Rakoczy --- bin/verilator | 1 + docs/guide/exe_verilator.rst | 12 +++++++-- src/V3Global.cpp | 2 +- src/V3Options.cpp | 1 + src/V3Options.h | 2 ++ src/V3ParseImp.cpp | 2 +- test_regress/t/t_preproc_resolve.out | 26 +++++++++++++++++++ test_regress/t/t_preproc_resolve.py | 29 ++++++++++++++++++++++ test_regress/t/t_preproc_resolve.v | 18 ++++++++++++++ test_regress/t/t_preproc_resolve/submod.sv | 20 +++++++++++++++ 10 files changed, 109 insertions(+), 4 deletions(-) create mode 100644 test_regress/t/t_preproc_resolve.out create mode 100755 test_regress/t/t_preproc_resolve.py create mode 100644 test_regress/t/t_preproc_resolve.v create mode 100644 test_regress/t/t_preproc_resolve/submod.sv diff --git a/bin/verilator b/bin/verilator index 0285e2df7..ee917f7a9 100755 --- a/bin/verilator +++ b/bin/verilator @@ -428,6 +428,7 @@ detailed descriptions of these arguments. --pipe-filter Filter all input through a script --pp-comments Show preprocessor comments with -E --prefix Name of top-level class + --preproc-resolve Include all found modules in the output with -E --preproc-token-limit Maximum tokens on a line allowed by preprocessor --private Debugging; see docs --prof-c Compile C++ code with profiling diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 155e93ce1..9259686bb 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -490,8 +490,8 @@ Summary: out. Beware of enabling debugging messages, as they will also go to standard out. See :vlopt:`--no-std`, which is implied by this. - See also :vlopt:`--dump-defines`, :vlopt:`-P`, and - :vlopt:`--pp-comments` options. + See also :vlopt:`--dump-defines`, :vlopt:`-P`, :vlopt:`--pp-comments` + and :vlopt:`--preproc-resolve` options. .. option:: --emit-accessors @@ -1152,6 +1152,14 @@ Summary: prepended to the name of the :vlopt:`--top` option, or V prepended to the first Verilog filename passed on the command line. +.. option:: --preproc-resolve + + With :vlopt:`-E`, include preprocessed output of submodules defined + in included files. Gives you a way of converting a multifile design + into a single file using include directory option. + + See :vlopt:`-E`. + .. option:: --preproc-token-limit Rarely needed. Configure the limit of the number of tokens Verilator diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 08bab7a2d..357f910b0 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -104,7 +104,7 @@ void V3Global::readFiles() { // v3Global.rootp()->dumpTreeFile(v3Global.debugFilename("parse.tree")); V3Error::abortIfErrors(); - if (!v3Global.opt.preprocOnly()) { + if (!v3Global.opt.preprocOnly() || v3Global.opt.preprocResolve()) { // Resolve all modules cells refer to V3LinkCells::link(v3Global.rootp(), &filter, &parseSyms); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index dd0176c84..0ef5207f7 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1504,6 +1504,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, validateIdentifier(fl, valp, "--prefix"); m_prefix = valp; }); + DECL_OPTION("-preproc-resolve", OnOff, &m_preprocResolve); DECL_OPTION("-preproc-token-limit", CbVal, [this, fl](const char* valp) { m_preprocTokenLimit = std::atoi(valp); if (m_preprocTokenLimit <= 0) fl->v3error("--preproc-token-limit must be > 0: " << valp); diff --git a/src/V3Options.h b/src/V3Options.h index 2e3c37da8..cae2e8cfa 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -219,6 +219,7 @@ private: V3StringSet m_fDfgPeepholeDisabled; // argument: -f[no-]dfg-peephole- bool m_preprocOnly = false; // main switch: -E + bool m_preprocResolve = false; // main switch: --preproc-resolve bool m_makePhony = false; // main switch: -MP bool m_preprocNoLine = false; // main switch: -P bool m_assert = false; // main switch: --assert @@ -470,6 +471,7 @@ public: bool preprocOnly() const { return m_preprocOnly; } bool makePhony() const { return m_makePhony; } bool preprocNoLine() const { return m_preprocNoLine; } + bool preprocResolve() const { return m_preprocResolve; } int preprocTokenLimit() const { return m_preprocTokenLimit; } bool underlineZero() const { return m_underlineZero; } string flags() const { return m_flags; } diff --git a/src/V3ParseImp.cpp b/src/V3ParseImp.cpp index c2cc2cf6f..4225a9c21 100644 --- a/src/V3ParseImp.cpp +++ b/src/V3ParseImp.cpp @@ -356,7 +356,7 @@ void V3ParseImp::parseFile(FileLine* fileline, const string& modfilename, bool i dumpInputsFile(); // Parse it - if (!v3Global.opt.preprocOnly()) { + if (!v3Global.opt.preprocOnly() || v3Global.opt.preprocResolve()) { lexFile(modfilename); } else { m_ppBuffers.clear(); diff --git a/test_regress/t/t_preproc_resolve.out b/test_regress/t/t_preproc_resolve.out new file mode 100644 index 000000000..7f76f5919 --- /dev/null +++ b/test_regress/t/t_preproc_resolve.out @@ -0,0 +1,26 @@ +`timescale 1ns/1ps +module top( + input logic clk, + input logic rst, + output logic top_out +); + submod u_submod ( + .clk (clk), + .rst (rst), + .out_signal(top_out) + ); +endmodule +`timescale 1ns/1ps +module submod( + input logic clk, + input logic rst, + output logic out_signal +); + always_ff @(posedge clk or posedge rst) begin + if (rst) begin + out_signal <= 1'b0; + end else begin + out_signal <= ~out_signal; + end + end +endmodule diff --git a/test_regress/t/t_preproc_resolve.py b/test_regress/t/t_preproc_resolve.py new file mode 100755 index 000000000..d6f95780f --- /dev/null +++ b/test_regress/t/t_preproc_resolve.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('vlt') + +stdout_filename = os.path.join(test.obj_dir, test.name + ".out") + +test.compile( + # Override default flags + v_flags=[''], + verilator_flags=["-E -P --preproc-resolve -y t/t_preproc_resolve"], + verilator_flags2=[''], + verilator_flags3=[''], + verilator_make_gmake=False, + make_top_shell=False, + make_main=False, + stdout_filename=stdout_filename) + +test.files_identical(stdout_filename, test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_preproc_resolve.v b/test_regress/t/t_preproc_resolve.v new file mode 100644 index 000000000..7d49cd8d0 --- /dev/null +++ b/test_regress/t/t_preproc_resolve.v @@ -0,0 +1,18 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps +module top(/*AUTOARG*/ + input logic clk, + input logic rst, + output logic top_out +); + submod u_submod (/*AUTOINST*/ + .clk (clk), + .rst (rst), + .out_signal(top_out) + ); +endmodule diff --git a/test_regress/t/t_preproc_resolve/submod.sv b/test_regress/t/t_preproc_resolve/submod.sv new file mode 100644 index 000000000..701cd7269 --- /dev/null +++ b/test_regress/t/t_preproc_resolve/submod.sv @@ -0,0 +1,20 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +`timescale 1ns/1ps +module submod(/*AUTOARG*/ + input logic clk, + input logic rst, + output logic out_signal +); + always_ff @(posedge clk or posedge rst) begin + if (rst) begin + out_signal <= 1'b0; + end else begin + out_signal <= ~out_signal; + end + end +endmodule From f7ccc013e7ffd5133d8a846d9c76ddf362731d73 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Fri, 21 Feb 2025 16:49:14 -0500 Subject: [PATCH 85/93] Improve V3EmitV for interfaces (#5796) --- src/V3Ast.cpp | 5 +++ src/V3AstNodeOther.h | 1 + src/V3EmitV.cpp | 39 +++++++++++++++++++++- test_regress/t/t_debug_emitv.out | 12 +++++++ test_regress/t/t_debug_emitv.v | 11 ++++-- test_regress/t/t_interface_virtual_bad.out | 6 ++-- test_regress/t/t_interface_wire_bad.out | 2 +- test_regress/t/t_json_only_tag.out | 2 +- test_regress/t/t_xml_tag.out | 2 +- 9 files changed, 70 insertions(+), 10 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index cf1873b6a..8767add80 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -196,6 +196,11 @@ string AstNode::prettyName(const string& namein) VL_PURE { pos += 7; continue; } + if (0 == std::strncmp(pos, "__Viftop", 8)) { + pretty += ""; + pos += 8; + continue; + } if (pos[0] == '_' && pos[1] == '_' && pos[2] == '0' && std::isxdigit(pos[3]) && std::isxdigit(pos[4])) { char value = 0; diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 91a1ab405..524c57bc9 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1252,6 +1252,7 @@ public: , m_name{name} { this->addVarsp(varsp); } + string verilogKwd() const override { return "modport"; } string name() const override VL_MT_STABLE { return m_name; } bool maybePointedTo() const override VL_MT_SAFE { return true; } ASTGEN_MEMBERS_AstModport; diff --git a/src/V3EmitV.cpp b/src/V3EmitV.cpp index bf4f6aba5..a9c75d9f5 100644 --- a/src/V3EmitV.cpp +++ b/src/V3EmitV.cpp @@ -726,6 +726,19 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { iterateConst(nodep->subDTypep()); // For post's key } } + void visit(AstIfaceRefDType* nodep) override { + if (m_arrayPost) { + puts(" ("); + if (nodep->cellp()) { + iterateConst(nodep->cellp()); + } else { + puts("????"); + } + puts(")"); + return; + } + puts(nodep->ifaceName()); + } void visit(AstRefDType* nodep) override { if (nodep->subDTypep()) { iterateConst(nodep->skipRefp()); @@ -733,6 +746,30 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { puts("\n???? // "s + nodep->prettyTypeName() + " -> UNLINKED\n"); } } + void visit(AstModport* nodep) override { + puts(nodep->verilogKwd()); + puts(" "); + puts(nodep->prettyName()); + puts(" (\n"); + if (nodep->varsp()) { + iterateConst(nodep->varsp()); + } else { + puts("????"); + } + puts(");\n"); + } + void visit(AstModportVarRef* nodep) override { + puts(nodep->direction().verilogKwd()); + puts(" "); + if (nodep->varp()) { + VL_RESTORER(m_suppressVarSemi); + m_suppressVarSemi = true; + iterateConst(nodep->varp()); + } else { + puts(nodep->prettyName()); + } + if (nodep->nextp()) puts(", "); + } void visit(AstNodeUOrStructDType* nodep) override { if (m_arrayPost) return; puts(nodep->verilogKwd() + " "); @@ -815,7 +852,7 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public EmitCBaseVisitorConst { } } void visit(AstVarXRef* nodep) override { - putfs(nodep, nodep->dotted()); + putfs(nodep, nodep->prettyName(nodep->dotted())); puts("."); if (nodep->varp()) { puts(nodep->varp()->prettyName()); diff --git a/test_regress/t/t_debug_emitv.out b/test_regress/t/t_debug_emitv.out index 77d14de96..fc093919b 100644 --- a/test_regress/t/t_debug_emitv.out +++ b/test_regress/t/t_debug_emitv.out @@ -37,6 +37,7 @@ module Vt_debug_emitv_t; disable label0; end endfunction + Iface the_ifaces[3:0] (); initial begin begin if ($test$plusargs(40'h48454c4c4f)) begin @@ -70,6 +71,9 @@ module Vt_debug_emitv_t; if ((| downto_32[60:54])) begin $write(""); end + if (the_ifaces[2].ifsig) begin + $write(""); + end end end bit [6:5] [4:3] [2:1] arraymanyd[10:11][12:13][14:15]; @@ -332,7 +336,15 @@ package Vt_debug_emitv___024unit; endfunction endclass endpackage +interface Vt_debug_emitv_Iface; + input logic clk; + logic ifsig; + modport mp ( + input logic ifsig + ); +endinterface module Vt_debug_emitv_sub; + input logic clk; task inc; input int signed i; output int signed o; diff --git a/test_regress/t/t_debug_emitv.v b/test_regress/t/t_debug_emitv.v index c6b1d91b3..c886cb300 100644 --- a/test_regress/t/t_debug_emitv.v +++ b/test_regress/t/t_debug_emitv.v @@ -25,7 +25,9 @@ class Cls; endfunction endclass -interface Iface; +interface Iface ( + input clk +); logic ifsig; modport mp(input ifsig); endinterface @@ -67,6 +69,8 @@ module t (/*AUTOARG*/ return value; endfunction + Iface the_ifaces [3:0] (.*); + initial begin if ($test$plusargs("HELLO")) $display("Hello argument found."); if (Pkg::FOO == 0) $write(""); @@ -80,6 +84,7 @@ module t (/*AUTOARG*/ if (|downto_32[48:40]) $write(""); if (|downto_32[55+:3]) $write(""); if (|downto_32[60-:7]) $write(""); + if (the_ifaces[2].ifsig) $write(""); end bit [6:5][4:3][2:1] arraymanyd[10:11][12:13][14:15]; @@ -117,7 +122,7 @@ module t (/*AUTOARG*/ return v == 0 ? 99 : ~v + 1; endfunction - sub sub(); + sub sub(.*); initial begin int other; @@ -251,7 +256,7 @@ module t (/*AUTOARG*/ end endmodule -module sub(); +module sub(input logic clk); task inc(input int i, output int o); o = {1'b0, i[31:1]} + 32'd1; endtask diff --git a/test_regress/t/t_interface_virtual_bad.out b/test_regress/t/t_interface_virtual_bad.out index 7149f62ba..84ac73793 100644 --- a/test_regress/t/t_interface_virtual_bad.out +++ b/test_regress/t/t_interface_virtual_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_interface_virtual_bad.v:31:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'q8__Viftop' is a different interface ('QBus'). +%Error: t/t_interface_virtual_bad.v:31:12: Operator ASSIGN expected 'PBus' interface on Assign RHS but 'q8' is a different interface ('QBus'). : ... note: In instance 't' 31 | v8 = q8; | ^~ @@ -6,7 +6,7 @@ : ... note: In instance 't' 35 | v8 = v8_phy; | ^~~~~~ -%Error: t/t_interface_virtual_bad.v:37:17: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface. +%Error: t/t_interface_virtual_bad.v:37:17: Operator ASSIGN expected non-interface on Assign RHS but 'p8' is an interface. : ... note: In instance 't' 37 | data = p8.phy; | ^~~ @@ -18,7 +18,7 @@ : ... note: In instance 't' 39 | data = v8; | ^~ -%Error: t/t_interface_virtual_bad.v:40:14: Operator ASSIGN expected non-interface on Assign RHS but 'p8__Viftop' is an interface. +%Error: t/t_interface_virtual_bad.v:40:14: Operator ASSIGN expected non-interface on Assign RHS but 'p8' is an interface. : ... note: In instance 't' 40 | data = p8; | ^~ diff --git a/test_regress/t/t_interface_wire_bad.out b/test_regress/t/t_interface_wire_bad.out index c792bf9b6..e98a4c000 100644 --- a/test_regress/t/t_interface_wire_bad.out +++ b/test_regress/t/t_interface_wire_bad.out @@ -1,4 +1,4 @@ -%Error: t/t_interface_wire_bad.v:17:20: Operator ASSIGNW expected non-interface on Assign RHS but 'a__Viftop' is an interface. +%Error: t/t_interface_wire_bad.v:17:20: Operator ASSIGNW expected non-interface on Assign RHS but 'a' is an interface. : ... note: In instance 't' 17 | wire wbad = sub.a; | ^ diff --git a/test_regress/t/t_json_only_tag.out b/test_regress/t/t_json_only_tag.out index d0864dfe4..20db5ea20 100644 --- a/test_regress/t/t_json_only_tag.out +++ b/test_regress/t/t_json_only_tag.out @@ -7,7 +7,7 @@ {"type":"VAR","name":"foo_op","addr":"(I)","loc":"d,16:11,16:17","dtypep":"(G)","origName":"foo_op","isSc":false,"isPrimaryIO":false,"direction":"OUTPUT","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"PORT","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"TYPEDEF","name":"my_struct","addr":"(J)","loc":"d,25:6,25:15","dtypep":"(K)","attrPublic":false,"childDTypep": [],"attrsp": []}, {"type":"CELL","name":"itop","addr":"(L)","loc":"d,29:8,29:12","origName":"itop","recursive":false,"modp":"(M)","pinsp": [],"paramsp": [],"rangep": [],"intfRefsp": []}, - {"type":"VAR","name":"itop__Viftop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, + {"type":"VAR","name":"itop","addr":"(N)","loc":"d,29:8,29:12","dtypep":"(O)","origName":"itop__Viftop","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"IFACEREF","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"this_struct","addr":"(P)","loc":"d,31:14,31:25","dtypep":"(Q)","origName":"this_struct","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"VAR","dtypeName":"","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"VAR","name":"dotted","addr":"(R)","loc":"d,33:16,33:22","dtypep":"(S)","origName":"dotted","isSc":false,"isPrimaryIO":false,"direction":"NONE","isConst":false,"isPullup":false,"isPulldown":false,"isUsedClock":false,"isSigPublic":false,"isLatched":false,"isUsedLoopIdx":false,"noReset":false,"attrIsolateAssign":false,"attrFileDescr":false,"isDpiOpenArray":false,"isFuncReturn":false,"isFuncLocal":false,"attrClocker":"UNKNOWN","lifetime":"VSTATIC","varType":"WIRE","dtypeName":"logic","isSigUserRdPublic":false,"isSigUserRWPublic":false,"isGParam":false,"isParam":false,"attrScBv":false,"attrSFormat":false,"sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}, {"type":"ASSIGNW","name":"","addr":"(T)","loc":"d,33:23,33:24","dtypep":"(S)", diff --git a/test_regress/t/t_xml_tag.out b/test_regress/t/t_xml_tag.out index cfc91c506..c342d280c 100644 --- a/test_regress/t/t_xml_tag.out +++ b/test_regress/t/t_xml_tag.out @@ -22,7 +22,7 @@ - + From 1857f6399c0ef38bc85f75df0dc1b6476419e941 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 21 Feb 2025 17:18:49 -0500 Subject: [PATCH 86/93] Fix `$monitor` with dotted references (#5794). --- Changes | 1 + src/V3Assert.cpp | 5 +- src/V3AstNodes.cpp | 2 +- test_regress/t/t_sys_monitor_dotted.py | 18 +++++ test_regress/t/t_sys_monitor_dotted.v | 94 ++++++++++++++++++++++++++ 5 files changed, 117 insertions(+), 3 deletions(-) create mode 100755 test_regress/t/t_sys_monitor_dotted.py create mode 100644 test_regress/t/t_sys_monitor_dotted.v diff --git a/Changes b/Changes index a13ab3d18..3ff0a8d24 100644 --- a/Changes +++ b/Changes @@ -57,6 +57,7 @@ Verilator 5.033 devel * Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.] * Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA] * Fix time import error on time parameters (#5786). [Luca Colagrande] +* Fix `$monitor` with dotted references (#5794). [Ahmed Elzeftawi] * Fix matching language extension options including dots. diff --git a/src/V3Assert.cpp b/src/V3Assert.cpp index 85d65a561..9ca686a7b 100644 --- a/src/V3Assert.cpp +++ b/src/V3Assert.cpp @@ -534,8 +534,9 @@ class AssertVisitor final : public VNVisitor { while (monExprsp) { if (AstNodeVarRef* varrefp = VN_CAST(monExprsp, NodeVarRef)) { AstSenItem* const senItemp - = new AstSenItem(fl, VEdgeType::ET_CHANGED, - new AstVarRef{fl, varrefp->varp(), VAccess::READ}); + = new AstSenItem{fl, VEdgeType::ET_CHANGED, + // Clone so get VarRef or VarXRef as needed + varrefp->cloneTree(false)}; if (!monSenItemsp) { monSenItemsp = senItemp; } else { diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index f0ee72c84..9e20b8b07 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -1787,7 +1787,7 @@ void AstClocking::dumpJson(std::ostream& str) const { } void AstDisplay::dump(std::ostream& str) const { this->AstNodeStmt::dump(str); - // str << " " << displayType().ascii(); + str << " [" << displayType().ascii() << "]"; } void AstDisplay::dumpJson(std::ostream& str) const { dumpJsonGen(str); } void AstEnumDType::dump(std::ostream& str) const { diff --git a/test_regress/t/t_sys_monitor_dotted.py b/test_regress/t/t_sys_monitor_dotted.py new file mode 100755 index 000000000..bd059b0f2 --- /dev/null +++ b/test_regress/t/t_sys_monitor_dotted.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +test.compile(verilator_flags2=['--binary']) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_sys_monitor_dotted.v b/test_regress/t/t_sys_monitor_dotted.v new file mode 100644 index 000000000..92ac9b4b0 --- /dev/null +++ b/test_regress/t/t_sys_monitor_dotted.v @@ -0,0 +1,94 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +interface addsub_ifc; + logic [7:0] a, b; + logic doAdd0; + logic clk; + logic rst_n; + logic [7:0] result; + logic overflow; +endinterface + +module adder_sub_8bit + ( + input logic clk, + input logic rst_n, + input logic [7:0] a, + input logic [7:0] b, + input logic doAdd0, + output logic [7:0] result, + output logic overflow + ); + + logic [7:0] b_modified; + logic [8:0] temp_result; + + assign b_modified = doAdd0 ? b : ~b + 8'b1; + + always_comb begin + temp_result = {1'b0, a} + {1'b0, b_modified}; + end + + always_ff @(posedge clk or negedge rst_n) begin + if (!rst_n) begin + result <= 8'h0; + overflow <= 1'b0; + end else begin + result <= temp_result[7:0]; + overflow <= (a[7] == b_modified[7] && result[7] != a[7]); + end + end + +endmodule + +module t; + addsub_ifc dut_ifc(); + + adder_sub_8bit dut + ( + .clk(dut_ifc.clk), + .rst_n(dut_ifc.rst_n), + .a(dut_ifc.a), + .b(dut_ifc.b), + .doAdd0(dut_ifc.doAdd0), + .result(dut_ifc.result), + .overflow(dut_ifc.overflow) + ); + + initial begin + dut_ifc.clk = 0; + forever #5 dut_ifc.clk = ~dut_ifc.clk; + end + + initial begin + dut_ifc.rst_n = 0; + dut_ifc.a = 8'h0; + dut_ifc.b = 8'h0; + dut_ifc.doAdd0 = 1'b1; + + #10 dut_ifc.rst_n = 1; + + #10; + dut_ifc.a = 8'h35; + dut_ifc.b = 8'h42; + dut_ifc.doAdd0 = 1'b1; + + #20; + $write("*-* All Finished *-*\n"); + $finish; + end + + initial begin + $display("[%0t] Initial rst_n=%b a=%h b=%h doAdd0=%b result=%h overflow=%b", + $time, dut_ifc.rst_n, dut_ifc.a, dut_ifc.b, + dut_ifc.doAdd0, dut_ifc.result, dut_ifc.overflow); + $monitor("[%0t] Monitor rst_n=%b a=%h b=%h doAdd0=%b result=%h overflow=%b", + $time, dut_ifc.rst_n, dut_ifc.a, dut_ifc.b, + dut_ifc.doAdd0, dut_ifc.result, dut_ifc.overflow); + end + +endmodule From 812861e7f2da1ded42ccbaf79c2704974368e1be Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Sat, 22 Feb 2025 19:57:36 +0000 Subject: [PATCH 87/93] Optimize splitting trigger computation and dump (#5798) --- src/V3Sched.cpp | 4 ++++ test_regress/t/t_flag_csplit_groups.py | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/V3Sched.cpp b/src/V3Sched.cpp index 76f6cfbdc..a166fcc0f 100644 --- a/src/V3Sched.cpp +++ b/src/V3Sched.cpp @@ -789,6 +789,10 @@ const TriggerKit createTriggers(AstNetlist* netlistp, AstCFunc* const initFuncp, // The debug code might leak signal names, so simply delete it when using --protect-ids if (v3Global.opt.protectIds()) dumpp->stmtsp()->unlinkFrBackWithNext()->deleteTree(); + // These might get large when we have a lot of triggers, so split if necessary + splitCheck(funcp); + splitCheck(dumpp); + return {vscp, funcp, dumpp, map}; } diff --git a/test_regress/t/t_flag_csplit_groups.py b/test_regress/t/t_flag_csplit_groups.py index 467fe6ad4..6ff731c8b 100755 --- a/test_regress/t/t_flag_csplit_groups.py +++ b/test_regress/t/t_flag_csplit_groups.py @@ -124,8 +124,8 @@ test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_clas test.file_grep_not(test.obj_dir + "/" + test.vm_prefix + "_classes.mk", "vm_classes_2") # Check combine count -test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (174 if test.vltmt else 156)) -test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', (2 if test.vltmt else 3)) +test.file_grep(test.stats, r'Node count, CFILE + (\d+)', (191 if test.vltmt else 173)) +test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_FAST + (\d+)', 2) test.file_grep(test.stats, r'Makefile targets, VM_CLASSES_SLOW + (\d+)', 2) test.passes() From 7ec32799b0d5374f1a7ad685d56739108b462166 Mon Sep 17 00:00:00 2001 From: Todd Strader Date: Sat, 22 Feb 2025 15:55:23 -0500 Subject: [PATCH 88/93] Fix expression coverage + VarXRefs (#5797) --- src/V3Coverage.cpp | 2 +- test_regress/t/t_cover_expr.out | 18 ++++++++++++++++++ test_regress/t/t_cover_expr.v | 12 ++++++++++++ test_regress/t/t_cover_expr_max.out | 18 ++++++++++++++++++ test_regress/t/t_cover_expr_trace.out | 18 ++++++++++++++++++ 5 files changed, 67 insertions(+), 1 deletion(-) diff --git a/src/V3Coverage.cpp b/src/V3Coverage.cpp index 2f81cdcf9..51881bee0 100644 --- a/src/V3Coverage.cpp +++ b/src/V3Coverage.cpp @@ -726,7 +726,7 @@ class CoverageVisitor final : public VNVisitor { // not be flagged as redundant or impossible, however the results will // still be valid, albeit messier for (CoverTerm& term : l) { - if (AstNodeVarRef* const refp = VN_CAST(term.m_exprp, NodeVarRef)) { + if (AstVarRef* const refp = VN_CAST(term.m_exprp, VarRef)) { varps[term.m_objective].insert(refp->varp()); } else { strs[term.m_objective].insert(term.m_emitV); diff --git a/test_regress/t/t_cover_expr.out b/test_regress/t/t_cover_expr.out index aaa7ae8bd..0a0f3ff76 100644 --- a/test_regress/t/t_cover_expr.out +++ b/test_regress/t/t_cover_expr.out @@ -43,6 +43,13 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + always @ (posedge clk) begin cyc <= cyc + 1; %000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); @@ -59,6 +66,13 @@ -000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t -000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t -000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t @@ -325,3 +339,7 @@ endmodule + interface intf(); + logic t; + endinterface + diff --git a/test_regress/t/t_cover_expr.v b/test_regress/t/t_cover_expr.v index 4b120982b..95cbe881e 100644 --- a/test_regress/t/t_cover_expr.v +++ b/test_regress/t/t_cover_expr.v @@ -37,10 +37,18 @@ module t (/*AUTOARG*/ return a & b; endfunction + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + always @ (posedge clk) begin cyc <= cyc + 1; if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); if ((~cyc2[32] && cyc2[33]) || (~cyc2[34] && cyc2[35])) $write(""); + if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); if ((~t1 && t2) || (~t3 && t4)) $write(""); if (t3 && (t1 == t2)) $write(""); if (123 == (124 - 32'(t1 || t2))) $write(""); @@ -163,3 +171,7 @@ module sub ( end endmodule + +interface intf(); + logic t; +endinterface diff --git a/test_regress/t/t_cover_expr_max.out b/test_regress/t/t_cover_expr_max.out index 4846df8cb..d01d1a412 100644 --- a/test_regress/t/t_cover_expr_max.out +++ b/test_regress/t/t_cover_expr_max.out @@ -43,6 +43,13 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + always @ (posedge clk) begin cyc <= cyc + 1; %000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); @@ -59,6 +66,13 @@ -000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t -000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t -000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t @@ -453,3 +467,7 @@ endmodule + interface intf(); + logic t; + endinterface + diff --git a/test_regress/t/t_cover_expr_trace.out b/test_regress/t/t_cover_expr_trace.out index aaa7ae8bd..0a0f3ff76 100644 --- a/test_regress/t/t_cover_expr_trace.out +++ b/test_regress/t/t_cover_expr_trace.out @@ -43,6 +43,13 @@ -000005 point: comment=(b==0) => 0 hier=top.t endfunction + localparam int num_intfs = 4; + intf the_intfs [num_intfs-1:0] (); + genvar intf_i; + for (intf_i = 0; intf_i < num_intfs; intf_i++) begin + always_comb the_intfs[intf_i].t = cyc[intf_i]; + end + always @ (posedge clk) begin cyc <= cyc + 1; %000004 if ((~cyc[0] && cyc[1]) || (~cyc[2] && cyc[3])) $write(""); @@ -59,6 +66,13 @@ -000002 point: comment=(cyc2[33]==0 && cyc2[34]==1) => 0 hier=top.t -000003 point: comment=(cyc2[33]==0 && cyc2[35]==0) => 0 hier=top.t -000002 point: comment=(cyc2[34]==0 && cyc2[35]==1) => 1 hier=top.t +%000004 if ((~the_intfs[0].t && the_intfs[1].t) || (~the_intfs[2].t && the_intfs[3].t)) $write(""); +-000002 point: comment=(the_intfs[0].t==0 && the_intfs[1].t==1) => 1 hier=top.t +-000002 point: comment=(the_intfs[0].t==1 && the_intfs[2].t==1) => 0 hier=top.t +-000004 point: comment=(the_intfs[0].t==1 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[1].t==0 && the_intfs[2].t==1) => 0 hier=top.t +-000003 point: comment=(the_intfs[1].t==0 && the_intfs[3].t==0) => 0 hier=top.t +-000002 point: comment=(the_intfs[2].t==0 && the_intfs[3].t==1) => 1 hier=top.t %000004 if ((~t1 && t2) || (~t3 && t4)) $write(""); -000002 point: comment=(t1==0 && t2==1) => 1 hier=top.t -000002 point: comment=(t1==1 && t3==1) => 0 hier=top.t @@ -325,3 +339,7 @@ endmodule + interface intf(); + logic t; + endinterface + From 4ce816427777710c9d20a3724db28b3ec1ddfdc2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 24 Feb 2025 03:51:49 -0500 Subject: [PATCH 89/93] Fix parsing input wire with default and range (#5800). --- Changes | 1 + src/verilog.y | 36 ++++++++++++------- test_regress/t/t_module_input_default_value.v | 15 ++++++++ 3 files changed, 39 insertions(+), 13 deletions(-) diff --git a/Changes b/Changes index 3ff0a8d24..0dffa0a82 100644 --- a/Changes +++ b/Changes @@ -58,6 +58,7 @@ Verilator 5.033 devel * Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA] * Fix time import error on time parameters (#5786). [Luca Colagrande] * Fix `$monitor` with dotted references (#5794). [Ahmed Elzeftawi] +* Fix parsing input wire with default and range (#5800). [RJ Cunningham] * Fix matching language extension options including dots. diff --git a/src/verilog.y b/src/verilog.y index c1e5a51c4..d41af1d6f 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -1479,13 +1479,13 @@ portsStarE: // IEEE: .* + list_of_ports + list_of_port_decla ; list_of_portsE: // IEEE: [ list_of_ports + list_of_port_declarations ] - portAndTagE { $$ = $1; } + portAndTagE { $$ = $1; } | list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); } ; list_of_ports: // IEEE: list_of_ports + list_of_port_declarations - portAndTag { $$ = $1; } - | list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); } + portAndTag { $$ = $1; } + | list_of_portsE ',' portAndTagE { $$ = addNextNull($1, $3); } ; portAndTagE: @@ -1584,33 +1584,43 @@ port: // ==IEEE: port // | portDirNetE data_type portSig variable_dimensionListE sigAttrListE { $$ = $3; VARDTYPE($2); VARIOANSI(); addNextNull($$, VARDONEP($$, $4, $5)); } + | portDirNetE data_type portSig variable_dimensionListE sigAttrListE '=' constExpr + { $$ = $3; VARDTYPE($2); VARIOANSI(); + if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } } | portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE { $$ = $4; VARDTYPE($3); VARIOANSI(); addNextNull($$, VARDONEP($$, $5, $6)); } + | portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr + { $$ = $4; VARDTYPE($3); VARIOANSI(); + if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } } | portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE { $$ = $4; VARDTYPE($3); VARIOANSI(); addNextNull($$, VARDONEP($$, $5, $6)); } + | portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr + { $$ = $4; VARDTYPE($3); VARIOANSI(); + if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } } | portDirNetE signing portSig variable_dimensionListE sigAttrListE { $$ = $3; AstNodeDType* const dtp = new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}; VARDTYPE_NDECL(dtp); VARIOANSI(); addNextNull($$, VARDONEP($$, $4, $5)); } + | portDirNetE signing portSig variable_dimensionListE sigAttrListE '=' constExpr + { $$ = $3; + AstNodeDType* const dtp = new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}; + VARDTYPE_NDECL(dtp); VARIOANSI(); + if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } } | portDirNetE signingE rangeList portSig variable_dimensionListE sigAttrListE { $$ = $4; AstNodeDType* const dtp = GRAMMARP->addRange( new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}, $3, true); VARDTYPE_NDECL(dtp); addNextNull($$, VARDONEP($$, $5, $6)); } + | portDirNetE signingE rangeList portSig variable_dimensionListE sigAttrListE '=' constExpr + { $$ = $4; + AstNodeDType* const dtp = GRAMMARP->addRange( + new AstBasicDType{$3->fileline(), LOGIC_IMPLICIT, $2}, $3, true); + VARDTYPE_NDECL(dtp); + if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } } | portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE { $$ = $2; /*VARDTYPE-same*/ addNextNull($$, VARDONEP($$, $3, $4)); } - // - | portDirNetE data_type portSig variable_dimensionListE sigAttrListE '=' constExpr - { $$ = $3; VARDTYPE($2); VARIOANSI(); - if (AstVar* vp = VARDONEP($$, $4, $5)) { addNextNull($$, vp); vp->valuep($7); } } - | portDirNetE yVAR data_type portSig variable_dimensionListE sigAttrListE '=' constExpr - { $$ = $4; VARDTYPE($3); VARIOANSI(); - if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } } - | portDirNetE yVAR implicit_typeE portSig variable_dimensionListE sigAttrListE '=' constExpr - { $$ = $4; VARDTYPE($3); VARIOANSI(); - if (AstVar* vp = VARDONEP($$, $5, $6)) { addNextNull($$, vp); vp->valuep($8); } } | portDirNetE /*implicit*/ portSig variable_dimensionListE sigAttrListE '=' constExpr { $$ = $2; /*VARDTYPE-same*/ if (AstVar* vp = VARDONEP($$, $3, $4)) { addNextNull($$, vp); vp->valuep($6); } } diff --git a/test_regress/t/t_module_input_default_value.v b/test_regress/t/t_module_input_default_value.v index 509dd0ad4..e14c222f4 100644 --- a/test_regress/t/t_module_input_default_value.v +++ b/test_regress/t/t_module_input_default_value.v @@ -43,6 +43,15 @@ module dut_default_input_logic32 endmodule +module dut_default_input_wire32 + ( + input wire [31:0] i = 32'h12345678, + output logic [31:0] o + ); + assign o = i; +endmodule + + module t (/*AUTOARG*/ // Inputs @@ -104,6 +113,11 @@ module t (.i(), // open .o(dut_logic32_o_open)); + logic [31:0] dut_wire32_o_open; + dut_default_input_wire32 u_dut_wire32_open + (.i(), // open + .o(dut_wire32_o_open)); + // 3. DUT instances with overriden values // instance names are u_dut*_overriden @@ -153,6 +167,7 @@ module t if (dut0_o_open != 0) $error; if (dut1_o_open != 1) $error; if (dut_logic32_o_open != 32'h1234_5678) $error; + if (dut_wire32_o_open != 32'h1234_5678) $error; // despite the port map override. At least the parameter goes through? $display("%t %m: outputs - overrides got {%0d %0d %0x} want {1 0 %0x}", From 5daf7385f158d8c851dd74f96225ef45deee4831 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 24 Feb 2025 04:00:37 -0500 Subject: [PATCH 90/93] Commentary: Changes update --- Changes | 8 ++++++-- docs/guide/exe_verilator.rst | 8 ++++---- docs/spelling.txt | 1 + 3 files changed, 11 insertions(+), 6 deletions(-) diff --git a/Changes b/Changes index 0dffa0a82..eb1991eef 100644 --- a/Changes +++ b/Changes @@ -11,16 +11,18 @@ contributors that suggested a given feature are shown in []. Thanks! Verilator 5.033 devel ========================== -**Major:** +**Important:** +* This is likely the last version to support Ubuntu 20.04, which is at end-of-life. * Add expression coverage (#4677) (#5719). [Todd Strader] -**Minor:** +**Other:** * Add `COVERIGN` warning, as a more specific UNSUPPORTED error. * Add `--public-ignore` to ignore public metacomments (#5716). [Andrew Nolte] * Add `--preproc-token-limit` (#5768). [Krzysztof Bieganski, Antmicro Ltd.] * Add lint error on importing package within a class (#5634) (#5679). [Nick Brereton] +* Add `--preproc-resolve` for modules in preprocessor output (#5789). [Kamil Rakoczy, Antmicro Ltd.] * Support multidimensional array access via VPI (#2812) (#5573). [Krzysztof Starecki] * Support nested classes (#4178) (#5778). [Kamil Rakoczy, Antmicro Ltd.] * Support VPI array accessors (#5612). [Moubarak Jeje] @@ -36,6 +38,7 @@ Verilator 5.033 devel * Improve `resetall support (#5728) (#5730). [Ethan Sifferman] * Optimize labels as final `if` block statements (#5744). * Optimize empty function definition bodies (#5750). +* Optimize splitting trigger computation and dump (#5798). [Geza Lore] * Fix error message when call task as a function (#3089). [Matthew Ballance] * Fix force VPI public signal visibility (#5225). [Frédéric Requin] * Fix VPI iteration over hierarchy (#5314) (#5731). [Natan Kreimer] @@ -57,6 +60,7 @@ Verilator 5.033 devel * Fix ignoring joins in stringify in preprocessor (#5777). [Krzysztof Bieganski, Antmicro Ltd.] * Fix unpacked split_var (#5782) (#5785). [Yutetsu TAKATSUKASA] * Fix time import error on time parameters (#5786). [Luca Colagrande] +* Fix dpi context functions (#5788). [Ryszard Rozak, Antmicro Ltd.] * Fix `$monitor` with dotted references (#5794). [Ahmed Elzeftawi] * Fix parsing input wire with default and range (#5800). [RJ Cunningham] * Fix matching language extension options including dots. diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index 9259686bb..d337d6795 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -1154,13 +1154,13 @@ Summary: .. option:: --preproc-resolve - With :vlopt:`-E`, include preprocessed output of submodules defined - in included files. Gives you a way of converting a multifile design - into a single file using include directory option. + With :vlopt:`-E`, resolve referenced instance modules, to include + preprocessed output of submodules. Used to convert a multi-file design + into a single output file. See :vlopt:`-E`. -.. option:: --preproc-token-limit +.. option:: --preproc-token-limit Rarely needed. Configure the limit of the number of tokens Verilator can process on a single line to prevent infinite loops and other hangs. diff --git a/docs/spelling.txt b/docs/spelling.txt index fc2dd5b6e..ac86ee4df 100644 --- a/docs/spelling.txt +++ b/docs/spelling.txt @@ -104,6 +104,7 @@ Eiler Eivind El Elbourne +Elzeftawi Embecosm Engstrom Enzo From dae4c8ee22f34ab83d54937f744d65cf3d529343 Mon Sep 17 00:00:00 2001 From: Yilou Wang Date: Mon, 24 Feb 2025 23:51:51 +0100 Subject: [PATCH 91/93] Support constrained random for arrays in structs (#5765) (#5802) --- include/verilated_random.h | 5 +- include/verilated_types.h | 2 +- src/V3EmitCHeaders.cpp | 47 +++++++ src/V3Randomize.cpp | 9 +- test_regress/t/t_constraint_struct_complex.py | 21 +++ test_regress/t/t_constraint_struct_complex.v | 128 ++++++++++++++++++ 6 files changed, 205 insertions(+), 7 deletions(-) create mode 100755 test_regress/t/t_constraint_struct_complex.py create mode 100755 test_regress/t/t_constraint_struct_complex.v diff --git a/include/verilated_random.h b/include/verilated_random.h index 7b1eaabbb..05389240f 100644 --- a/include/verilated_random.h +++ b/include/verilated_random.h @@ -347,9 +347,8 @@ public: void modifyMembers(T& obj, std::index_sequence, std::string baseName) { // Use the indices to access each member via std::get (void)std::initializer_list{ - (write_var(std::get(obj.getMembers(obj)), - sizeof(std::get(obj.getMembers(obj))) * 8, - (baseName + "." + obj.memberNames()[I]).c_str(), 0), + (write_var(std::get(obj.getMembers(obj)), obj.memberWidth()[I], + (baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]), 0)...}; } diff --git a/include/verilated_types.h b/include/verilated_types.h index 3f8b7006d..cd5ea552e 100644 --- a/include/verilated_types.h +++ b/include/verilated_types.h @@ -954,7 +954,7 @@ public: VlAssocArray& operator=(VlAssocArray&&) = default; bool operator==(const VlAssocArray& rhs) const { return m_map == rhs.m_map; } bool operator!=(const VlAssocArray& rhs) const { return m_map != rhs.m_map; } - + bool operator<(const VlAssocArray& rhs) const { return m_map < rhs.m_map; } // METHODS T_Value& atDefault() { return m_defaultValue; } const T_Value& atDefault() const { return m_defaultValue; } diff --git a/src/V3EmitCHeaders.cpp b/src/V3EmitCHeaders.cpp index 41c471f0a..b4acd5d48 100644 --- a/src/V3EmitCHeaders.cpp +++ b/src/V3EmitCHeaders.cpp @@ -240,6 +240,45 @@ class EmitCHeader final : public EmitCConstInit { emitUnpackedUOrSBody(sdtypep); } } + enum class AttributeType { Width, Dimension }; + // Get member attribute based on type + int getNodeAttribute(const AstMemberDType* itemp, AttributeType type) { + const bool isArrayType + = VN_IS(itemp->dtypep(), UnpackArrayDType) || VN_IS(itemp->dtypep(), DynArrayDType) + || VN_IS(itemp->dtypep(), QueueDType) || VN_IS(itemp->dtypep(), AssocArrayDType); + switch (type) { + case AttributeType::Width: { + if (isArrayType) { + // For arrays, get innermost element width + AstNodeDType* dtype = itemp->dtypep(); + while (dtype->subDTypep()) dtype = dtype->subDTypep(); + return dtype->width(); + } + return itemp->width(); + } + case AttributeType::Dimension: { + // Return array dimension or 0 for non-arrays + return isArrayType ? itemp->dtypep()->dimensions(true).second : 0; + } + default: { + return 0; + } + } + } + template + void emitMemberVector(const AstNodeUOrStructDType* sdtypep) { + puts("return {"); + bool needComma = false; + for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; + itemp = VN_AS(itemp->nextp(), MemberDType)) { + if (!itemp->isConstrainedRand()) continue; + // Comma handling: add before element except first + if (needComma) puts(",\n"); + putns(itemp, std::to_string(getNodeAttribute(itemp, T))); + needComma = true; + } + puts("};\n}\n"); + } void emitUnpackedUOrSBody(AstNodeUOrStructDType* sdtypep) { putns(sdtypep, sdtypep->verilogKwd()); // "struct"/"union" puts(" " + EmitCBase::prefixNameProtect(sdtypep) + " {\n"); @@ -252,6 +291,8 @@ class EmitCHeader final : public EmitCConstInit { // - memberNames: Get member names // - getMembers: Access member references // - memberIndices: Retrieve member indices + // - memberWidth: Retrieve member width + // - memberDimension: Retrieve member dimension if (sdtypep->isConstrainedRand()) { putns(sdtypep, "\nstd::vector memberNames(void) const {\n"); puts("return {"); @@ -263,6 +304,12 @@ class EmitCHeader final : public EmitCConstInit { } puts("};\n}\n"); + putns(sdtypep, "\nstd::vector memberWidth(void) const {\n"); + emitMemberVector(sdtypep); + + putns(sdtypep, "\nstd::vector memberDimension(void) const {\n"); + emitMemberVector(sdtypep); + putns(sdtypep, "\nauto memberIndices(void) const {\n"); puts("return std::index_sequence_for<"); for (const AstMemberDType* itemp = sdtypep->membersp(); itemp; diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 909cfe726..1f1842e05 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -712,9 +712,12 @@ class ConstraintExprVisitor final : public VNVisitor { } void visit(AstStructSel* nodep) override { if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) { - AstMemberDType* memberp - = VN_AS(nodep->fromp()->dtypep()->skipRefp(), StructDType)->membersp(); - while (memberp->nextp()) { + AstNodeExpr* const fromp = nodep->fromp(); + if (VN_IS(fromp, StructSel)) { + VN_AS(fromp->dtypep()->skipRefp(), StructDType)->markConstrainedRand(true); + } + AstMemberDType* memberp = VN_AS(fromp->dtypep()->skipRefp(), StructDType)->membersp(); + while (memberp) { if (memberp->name() == nodep->name()) { memberp->markConstrainedRand(true); break; diff --git a/test_regress/t/t_constraint_struct_complex.py b/test_regress/t/t_constraint_struct_complex.py new file mode 100755 index 000000000..466368b3d --- /dev/null +++ b/test_regress/t/t_constraint_struct_complex.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_struct_complex.v b/test_regress/t/t_constraint_struct_complex.v new file mode 100755 index 000000000..b760bd375 --- /dev/null +++ b/test_regress/t/t_constraint_struct_complex.v @@ -0,0 +1,128 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by PlanV GmbH. +// SPDX-License-Identifier: CC0-1.0 + +class ArrayStruct; + /* verilator lint_off SIDEEFFECT */ + // Struct with an unpacked array + typedef int arr_3_t[3]; + typedef int arr_4_t[4]; + typedef struct { + rand arr_3_t arr_3; + arr_4_t arr_4; + rand int arr[3]; + } unpacked_struct_t; + + // Struct with a dynamic array + typedef struct { + rand int arr[]; + } dynamic_struct_t; + + // Struct with a queue + typedef struct { + rand int arr[$]; + } queue_struct_t; + + // Struct with an associative array (string as index) + typedef struct { + rand int arr[string]; + } associative_struct_t; + + // Struct with a multi-dimensional array + typedef struct { + rand int arr[2][3]; + } multi_dim_struct_t; + + // Struct with a mix of dynamic and unpacked arrays + typedef struct { + rand int mix_arr[3][]; + } mixed_struct_t; + + rand unpacked_struct_t s1; + rand dynamic_struct_t s2; + rand queue_struct_t s3; + rand associative_struct_t s4; + rand multi_dim_struct_t s5; + rand mixed_struct_t s6; + + constraint c_unpacked { + foreach (s1.arr[i]) s1.arr[i] inside {1, 2, 3, 4}; + foreach (s1.arr_3[i]) s1.arr_3[i] inside {11, 22, 33, 44, 55}; + } + constraint c_dynamic { foreach (s2.arr[i]) s2.arr[i] inside {[10:20]}; } + constraint c_queue { foreach (s3.arr[i]) s3.arr[i] inside {[100:200]}; } + constraint c_assoc { + s4.arr["one"] inside {[10:50]}; + s4.arr["two"] inside {[51:100]}; + s4.arr["three"] inside {[101:150]}; + } + constraint c_multi_dim { foreach (s5.arr[i, j]) s5.arr[i][j] inside {[0:9]}; } + constraint c_mix { + foreach (s6.mix_arr[i, j]) s6.mix_arr[i][j] inside {[50:100]}; + } + + function new(); + s1.arr = '{1, 2, 3}; + s1.arr_3 = '{1, 2, 3}; + s1.arr_4 = '{0, 2, 3, 4}; + s2.arr = new[3]; + foreach(s2.arr[i]) begin + s2.arr[i] = 'h0 + i; + end + s3.arr.push_back(100); + s3.arr.push_back(200); + s3.arr.push_back(300); + s4.arr["one"] = 1000; + s4.arr["two"] = 2000; + s4.arr["three"] = 3000; + s5.arr = '{ '{default:0}, '{default:0} }; + foreach (s6.mix_arr[i]) begin + s6.mix_arr[i] = new[i + 1]; + end + endfunction + + function void print(); + foreach (s1.arr[i]) $display("s1.arr[%0d] = %0d", i, s1.arr[i]); + foreach (s1.arr_3[i]) $display("s1.arr_3[%0d] = %0d", i, s1.arr_3[i]); + foreach (s1.arr_4[i]) $display("s1.arr_4[%0d] = %0d", i, s1.arr_4[i]); + foreach (s2.arr[i]) $display("s2.arr[%0d] = %0d", i, s2.arr[i]); + foreach (s3.arr[i]) $display("s3.arr[%0d] = %0d", i, s3.arr[i]); + foreach (s4.arr[i]) $display("s4.arr[\"%s\"] = %0d", i, s4.arr[i]); + foreach (s5.arr[i, j]) $display("s5.arr[%0d][%0d] = %0d", i, j, s5.arr[i][j]); + foreach (s6.mix_arr[i, j]) $display("s6.mix_arr[%0d][%0d] = %0d", i, j, s6.mix_arr[i][j]); + endfunction + + // Self-test function to verify constraints + function void self_test(); + foreach (s1.arr[i]) if (!(s1.arr[i] inside {1, 2, 3, 4})) $stop; + foreach (s1.arr_3[i]) if (!(s1.arr_3[i] inside {11, 22, 33, 44, 55})) $stop; + // Note: s1.arr_4[0] is not rand + if ((s1.arr_4[0] != 0) || (s1.arr_4[1] != 2) || (s1.arr_4[2] != 3) || (s1.arr_4[3] != 4)) $stop; + foreach (s2.arr[i]) if (!(s2.arr[i] inside {[10:20]})) $stop; + foreach (s3.arr[i]) if (!(s3.arr[i] inside {[100:200]})) $stop; + if (!(s4.arr["one"] inside {[10:50]})) $stop; + if (!(s4.arr["two"] inside {[51:100]})) $stop; + if (!(s4.arr["three"] inside {[101:150]})) $stop; + foreach (s5.arr[i, j]) if (!(s5.arr[i][j] inside {[0:9]})) $stop; + foreach (s6.mix_arr[i]) if (s6.mix_arr[i].size() == 0) $stop; + foreach (s6.mix_arr[i, j]) if (!(s6.mix_arr[i][j] inside {[50:100]})) $stop; + endfunction + /* verilator lint_off SIDEEFFECT */ +endclass + +module t_constraint_struct_complex; + int success; + ArrayStruct asc; + initial begin + asc = new(); + success = asc.randomize(); + if (success != 1) $stop; + asc.self_test(); + // asc.print(); + $write("*-* All Finished *-*\n"); + $finish; + end + +endmodule From c1f078766956c77d3e6ddaa06d1d0b26177bf184 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 24 Feb 2025 18:15:08 -0500 Subject: [PATCH 92/93] Commentary: Changes update --- Changes | 1 + 1 file changed, 1 insertion(+) diff --git a/Changes b/Changes index eb1991eef..e9533d497 100644 --- a/Changes +++ b/Changes @@ -31,6 +31,7 @@ Verilator 5.033 devel * Support constraints on associative array user-defined keys (#5671) (#5729). [Yilou Wang] * Support `+incdir` with multiple directories. * Support integer atom type ports in `--hierarchical` (#5748). [Bartłomiej Chmiel, Antmicro Ltd.] +* Support constrained random for arrays in structs (#5765) (#5802). [Yilou Wang] * Support selects on arbitrary string expressions (#5773). [Krzysztof Bieganski, Antmicro Ltd.] * Support side effects of form 'variable[index_function()]++'. * Improve hierarchical DPI wrapper scheduling performance (#2583) (#5734). [Bartłomiej Chmiel, Antmicro Ltd.] From 436d3d76f0ff841cc0d1e3fdf0c888ebf40137b2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Mon, 24 Feb 2025 18:17:00 -0500 Subject: [PATCH 93/93] Version bump --- CMakeLists.txt | 4 ++-- Changes | 2 +- configure.ac | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index d53b2f664..ee1260857 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,14 +16,14 @@ cmake_minimum_required(VERSION 3.15) cmake_policy(SET CMP0091 NEW) # Use MSVC_RUNTIME_LIBRARY to select the runtime project( Verilator - VERSION 5.033 + VERSION 5.034 HOMEPAGE_URL https://verilator.org LANGUAGES CXX ) # Set default build type to Release if not specified if(NOT CMAKE_BUILD_TYPE) - set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) + set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Build type" FORCE) endif() option( diff --git a/Changes b/Changes index e9533d497..f1bfb1c31 100644 --- a/Changes +++ b/Changes @@ -8,7 +8,7 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! -Verilator 5.033 devel +Verilator 5.034 2025-02-24 ========================== **Important:** diff --git a/configure.ac b/configure.ac index 4d815fc0b..26a0ba45c 100644 --- a/configure.ac +++ b/configure.ac @@ -10,7 +10,7 @@ # Then 'make maintainer-dist' #AC_INIT([Verilator],[#.### YYYY-MM-DD]) #AC_INIT([Verilator],[#.### devel]) -AC_INIT([Verilator],[5.033 devel], +AC_INIT([Verilator],[5.034 2025-02-24], [https://verilator.org], [verilator],[https://verilator.org])