From a999c73ce080c08461d994897c06bd1d7a4bd890 Mon Sep 17 00:00:00 2001 From: Geza Lore Date: Wed, 28 Sep 2022 14:41:26 +0100 Subject: [PATCH 01/12] Commentary --- src/V3GraphAlg.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/V3GraphAlg.cpp b/src/V3GraphAlg.cpp index 0e5042b5a..b74de1351 100644 --- a/src/V3GraphAlg.cpp +++ b/src/V3GraphAlg.cpp @@ -178,7 +178,10 @@ private: std::vector m_callTrace; // List of everything we hit processing so far void main() { - // Use Tarjan's algorithm to find the strongly connected subgraphs. + // Use Pearce's algorithm to color the strongly connected components. For reference see + // "An Improved Algorithm for Finding the Strongly Connected Components of a Directed + // Graph", David J.Pearce, 2005 + // // Node State: // Vertex::user // DFS number indicating possible root of subtree, 0=not iterated // Vertex::color // Output subtree number (fully processed) From f25a9a4c8067a3291c6268a4be7b0acd461d166d Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Wed, 28 Sep 2022 19:11:00 -0400 Subject: [PATCH 02/12] examples: Use SC_ZERO_TIME in tracing (#3646) --- examples/make_tracing_sc/sc_main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/make_tracing_sc/sc_main.cpp b/examples/make_tracing_sc/sc_main.cpp index 3d3cb1392..a23040a54 100644 --- a/examples/make_tracing_sc/sc_main.cpp +++ b/examples/make_tracing_sc/sc_main.cpp @@ -82,7 +82,7 @@ int sc_main(int argc, char* argv[]) { // You must do one evaluation before enabling waves, in order to allow // SystemC to interconnect everything for testing. - sc_start(1, SC_NS); + sc_start(SC_ZERO_TIME); #if VM_TRACE // If verilator was invoked with --trace argument, From fa4b10b4d9a4513fb8914c5c7af5b6f917b29878 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 30 Sep 2022 23:03:26 -0400 Subject: [PATCH 03/12] Commentary: Changes update --- Changes | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index ed4497f7f..b793b69cb 100644 --- a/Changes +++ b/Changes @@ -19,14 +19,18 @@ Verilator 4.227 devel **Minor:** -* Support IEEE constant signal strengths (#3601). [Ryszard Rozak/Antmicro] +* Support some IEEE signal strengths (#3601) (#3629). [Ryszard Rozak/Antmicro] * Add --main to generate main() C++ (previously was experimental only). +* Add --build-jobs, and rework arguments for -j (#3623). [Kamil Rakoczy] * Rename --bin to --build-dep-bin. +* Rename debug flags --dumpi-tree, --dumpi-graph, etc. [Geza Lore] * Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic] * Fix crash in gate optimization of circular logic (#3543). [Bill Flynn] * Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk] * Fix default --mod-prefix when --prefix is repeated (#3603). [Geza Lore] +* Fix calling trace() after open() segfault (#3610) (#3627). [Yu-Sheng Lin] * Fix typedef'ed class conversion to boolean (#3616). [Aleksander Kiryk] +* Fix Verilation speed when disabled warnings (#3632). [Kamil Rakoczy/Antmicro] Verilator 4.226 2022-08-31 @@ -41,6 +45,7 @@ Verilator 4.226 2022-08-31 * Support $test$plusargs(expr) (#3489). * Rename trace rolloverSize() (#3570). * Improve Verilation speed with --threads on large designs. [Geza Lore] +* Improve Verilation memory by reducing V3Number (#3521). [Mariusz Glebocki/Antmicro] * Fix struct pattern assignment (#2328) (#3517). [Mostafa Gamal] * Fix public combo propagation issues (#2905). [Todd Strader] * Fix incorrect tristate logic (#3399) [shareefj, Vighnesh Iyer] From 5ed882faf2d12535853f17922e7220f5c1368eb2 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Fri, 30 Sep 2022 23:41:35 -0400 Subject: [PATCH 04/12] Fix unused compiler warning when not VL_THREADED. --- include/verilated_trace_imp.h | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/verilated_trace_imp.h b/include/verilated_trace_imp.h index 0663986f1..90dcc2e70 100644 --- a/include/verilated_trace_imp.h +++ b/include/verilated_trace_imp.h @@ -551,15 +551,15 @@ template <> void VerilatedTrace::runOffloadedCallbacks( const std::vector& cbVec) { // Fall back on sequential execution - for (const CallbackRecord& cbr : cbVec) { #ifdef VL_THREADED + for (const CallbackRecord& cbr : cbVec) { Buffer* traceBufferp = getTraceBuffer(); cbr.m_dumpOffloadCb(cbr.m_userp, static_cast(traceBufferp)); commitTraceBuffer(traceBufferp); -#else - VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable"); -#endif } +#else + if (!cbVec.empty()) VL_FATAL_MT(__FILE__, __LINE__, "", "Unreachable"); +#endif } template <> @@ -586,7 +586,9 @@ void VerilatedTrace::dump(uint64_t timeui) VL_MT_SAFE_EXCLUD if (!preChangeDump()) return; } +#ifdef VL_THREADED uint32_t* bufferp = nullptr; +#endif if (offload()) { #ifdef VL_THREADED // Currently only incremental dumps run on the worker thread From 746c7ea8f786e1183f15c28727cbc0c44e9b556e Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 1 Oct 2022 08:28:27 -0400 Subject: [PATCH 05/12] Version bump --- Changes | 2 +- configure.ac | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Changes b/Changes index b793b69cb..06ce093d1 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 4.227 devel +Verilator 4.228 2022-10-01 ========================== **Announcement:** diff --git a/configure.ac b/configure.ac index 7caacbd45..35b3fcd01 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],[4.227 devel], +AC_INIT([Verilator],[4.228 2022-10-01], [https://verilator.org], [verilator],[https://verilator.org]) From 0b843ada03f1d3d0ef3dec9dfa1ae2dd16fcffd5 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 1 Oct 2022 08:34:43 -0400 Subject: [PATCH 06/12] devel release --- Changes | 5 +++++ configure.ac | 2 +- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Changes b/Changes index 06ce093d1..e44303b8a 100644 --- a/Changes +++ b/Changes @@ -8,6 +8,11 @@ The changes in each Verilator version are described below. The contributors that suggested a given feature are shown in []. Thanks! +Verilator 4.229 devel +========================== + + + Verilator 4.228 2022-10-01 ========================== diff --git a/configure.ac b/configure.ac index 35b3fcd01..5e8505df9 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],[4.228 2022-10-01], +AC_INIT([Verilator],[4.229 devel], [https://verilator.org], [verilator],[https://verilator.org]) From 4db998d357595f02c0503f8d166c6fb40b8f67d4 Mon Sep 17 00:00:00 2001 From: Wilson Snyder Date: Sat, 1 Oct 2022 10:09:14 -0400 Subject: [PATCH 07/12] CI: coverage on 22.04 --- .github/workflows/coverage.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/coverage.yml b/.github/workflows/coverage.yml index 506dd98a1..b88e1f05f 100644 --- a/.github/workflows/coverage.yml +++ b/.github/workflows/coverage.yml @@ -24,10 +24,10 @@ jobs: Build: - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 env: CI_BUILD_STAGE_NAME: build - CI_RUNS_ON: ubuntu-20.04 + CI_RUNS_ON: ubuntu-22.04 steps: - name: Checkout @@ -73,11 +73,11 @@ jobs: - 9 include: - { test: dist, num: '' } - runs-on: ubuntu-20.04 + runs-on: ubuntu-22.04 name: test-${{ matrix.test }}${{ matrix.num }} env: CI_BUILD_STAGE_NAME: test - CI_RUNS_ON: ubuntu-20.04 + CI_RUNS_ON: ubuntu-22.04 steps: - name: Download tar archive From 46b8dca3602111f464053a4ce97c84fbc830858a Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Sat, 1 Oct 2022 16:34:30 +0200 Subject: [PATCH 08/12] Add handling of tristate select/extend (#3604) --- src/V3Tristate.cpp | 75 +++++++++++++++++++++------ test_regress/t/t_tri_and_eqcase.out | 8 +++ test_regress/t/t_tri_and_eqcase.pl | 19 +++++++ test_regress/t/t_tri_and_eqcase.v | 17 ++++++ test_regress/t/t_tri_select_eqcase.pl | 21 ++++++++ test_regress/t/t_tri_select_eqcase.v | 26 ++++++++++ 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 test_regress/t/t_tri_and_eqcase.out create mode 100755 test_regress/t/t_tri_and_eqcase.pl create mode 100644 test_regress/t/t_tri_and_eqcase.v create mode 100755 test_regress/t/t_tri_select_eqcase.pl create mode 100644 test_regress/t/t_tri_select_eqcase.v diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 55dddaacc..7e35067eb 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -493,6 +493,47 @@ class TristateVisitor final : public TristateBaseVisitor { } return VN_AS(invarp->user1p(), Var); } + AstConst* getNonZConstp(AstConst* const constp) { + FileLine* const fl = constp->fileline(); + V3Number numz{constp, constp->width()}; + numz.opBitsZ(constp->num()); // Z->1, else 0 + V3Number numz0{constp, constp->width()}; + numz0.opNot(numz); // Z->0, else 1 + return new AstConst{fl, numz0}; + } + AstNode* getEnExprBasedOnOriginalp(AstNode* const nodep) { + if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) { + return new AstVarRef{varrefp->fileline(), getCreateEnVarp(varrefp->varp()), + VAccess::READ}; + } else if (AstConst* const constp = VN_CAST(nodep, Const)) { + return getNonZConstp(constp); + } else if (AstExtend* const extendp = VN_CAST(nodep, Extend)) { + // Extend inserts 0 at the beginning. 0 in __en variable means that this bit equals z, + // so in order to preserve the value of the original AstExtend node we should insert 1 + // instead of 0. To extend __en expression we have to negate its lhsp() and then negate + // whole extend. + + // Unlink lhsp before copying to save unnecessary copy of lhsp + AstNode* const lhsp = extendp->lhsp()->unlinkFrBack(); + AstExtend* const enExtendp = extendp->cloneTree(false); + extendp->lhsp(lhsp); + AstNode* const enLhsp = getEnExprBasedOnOriginalp(lhsp); + enExtendp->lhsp(new AstNot{enLhsp->fileline(), enLhsp}); + return new AstNot{enExtendp->fileline(), enExtendp}; + } else if (AstSel* const selp = VN_CAST(nodep, Sel)) { + AstNode* const fromp = selp->fromp()->unlinkFrBack(); + AstSel* const enSelp = selp->cloneTree(false); + selp->fromp(fromp); + AstNode* const enFromp = getEnExprBasedOnOriginalp(fromp); + enSelp->fromp(enFromp); + return enSelp; + } else { + nodep->v3warn(E_UNSUPPORTED, + "Unsupported tristate construct: " << nodep->prettyTypeName() + << " in function " << __func__); + return nullptr; + } + } AstVar* getCreateOutVarp(AstVar* invarp) { // Return the master __out for the specified input variable if (!invarp->user4p()) { @@ -959,14 +1000,10 @@ class TristateVisitor final : public TristateBaseVisitor { } else if (m_tgraph.isTristate(nodep)) { m_tgraph.didProcess(nodep); FileLine* const fl = nodep->fileline(); - V3Number numz(nodep, nodep->width()); - numz.opBitsZ(nodep->num()); // Z->1, else 0 - V3Number numz0(nodep, nodep->width()); - numz0.opNot(numz); // Z->0, else 1 - V3Number num1(nodep, nodep->width()); - num1.opAnd(nodep->num(), numz0); // 01X->01X, Z->0 - AstConst* const newconstp = new AstConst(fl, num1); - AstConst* const enp = new AstConst(fl, numz0); + AstConst* const enp = getNonZConstp(nodep); + V3Number num1{nodep, nodep->width()}; + num1.opAnd(nodep->num(), enp->num()); // 01X->01X, Z->0 + AstConst* const newconstp = new AstConst{fl, num1}; nodep->replaceWith(newconstp); VL_DO_DANGLING(pushDeletep(nodep), nodep); newconstp->user1p(enp); // Propagate up constant with non-Z bits as 1 @@ -1263,23 +1300,27 @@ class TristateVisitor final : public TristateBaseVisitor { UINFO(9, dbgState() << nodep << endl); // Constification always moves const to LHS AstConst* const constp = VN_CAST(nodep->lhsp(), Const); - AstVarRef* const varrefp = VN_CAST(nodep->rhsp(), VarRef); // Input variable - if (constp && constp->user1p() && varrefp) { + if (constp && constp->user1p()) { // 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in)) - varrefp->unlinkFrBack(); + AstNode* const rhsp = nodep->rhsp(); + rhsp->unlinkFrBack(); FileLine* const fl = nodep->fileline(); + AstNode* enRhsp; + if (rhsp->user1p()) { + enRhsp = rhsp->user1p(); + rhsp->user1p(nullptr); + } else { + enRhsp = getEnExprBasedOnOriginalp(rhsp); + } const V3Number oneIfEn = VN_AS(constp->user1p(), Const) ->num(); // visit(AstConst) already split into en/ones const V3Number& oneIfEnOne = constp->num(); - AstVar* const envarp = getCreateEnVarp(varrefp->varp()); AstNode* newp - = new AstLogAnd(fl, - new AstEq(fl, new AstConst(fl, oneIfEn), - new AstVarRef(fl, envarp, VAccess::READ)), + = new AstLogAnd{fl, new AstEq{fl, new AstConst{fl, oneIfEn}, enRhsp}, // Keep the caseeq if there are X's present - new AstEqCase(fl, new AstConst(fl, oneIfEnOne), varrefp)); - if (neq) newp = new AstLogNot(fl, newp); + new AstEqCase{fl, new AstConst{fl, oneIfEnOne}, rhsp}}; + if (neq) newp = new AstLogNot{fl, newp}; UINFO(9, " newceq " << newp << endl); if (debug() >= 9) nodep->dumpTree(cout, "-caseeq-old: "); if (debug() >= 9) newp->dumpTree(cout, "-caseeq-new: "); diff --git a/test_regress/t/t_tri_and_eqcase.out b/test_regress/t/t_tri_and_eqcase.out new file mode 100644 index 000000000..315a41e1f --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.out @@ -0,0 +1,8 @@ +%Error-UNSUPPORTED: t/t_tri_and_eqcase.v:9:28: Unsupported tristate construct: AND in function getEnExprBasedOnOriginalp + 9 | logic b = 1'z === (clk1 & clk2); + | ^ + ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest +%Error: Internal Error: t/t_tri_and_eqcase.v:9:18: ../V3Ast.cpp:#: Null item passed to setOp2p + 9 | logic b = 1'z === (clk1 & clk2); + | ^~~ + ... See the manual at https://verilator.org/verilator_doc.html for more assistance. diff --git a/test_regress/t/t_tri_and_eqcase.pl b/test_regress/t/t_tri_and_eqcase.pl new file mode 100755 index 000000000..48bf31461 --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +lint( + fails => $Self->{vlt_all}, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_tri_and_eqcase.v b/test_regress/t/t_tri_and_eqcase.v new file mode 100644 index 000000000..204aab82d --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.v @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (clk1, clk2); + input wire clk1, clk2; + logic b = 1'z === (clk1 & clk2); + + always begin + if (!b) begin + $write("*-* All Finished *-*\n"); + $finish; + end + end +endmodule diff --git a/test_regress/t/t_tri_select_eqcase.pl b/test_regress/t/t_tri_select_eqcase.pl new file mode 100755 index 000000000..f5e338520 --- /dev/null +++ b/test_regress/t/t_tri_select_eqcase.pl @@ -0,0 +1,21 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2022 by Antmicro Ltd. 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 + +scenarios(simulator => 1); + +compile( + ); + +execute( + check_finished => 1, + ); + +ok(1); +1; diff --git a/test_regress/t/t_tri_select_eqcase.v b/test_regress/t/t_tri_select_eqcase.v new file mode 100644 index 000000000..c70688c0f --- /dev/null +++ b/test_regress/t/t_tri_select_eqcase.v @@ -0,0 +1,26 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2022 by Antmicro Ltd. +// SPDX-License-Identifier: CC0-1.0 + +module t (/*AUTOARG*/); + wire [3:0] a = 4'b11z1; + logic b = 1'bz === a[1]; + logic c = 1'bz === a[2]; + logic d = 2'bzz === 2'(a[1]); + logic e = 2'b0z === 2'(a[1]); + + + always begin + if (b && !c && !d && e) begin + $write("*-* All Finished *-*\n"); + $finish; + end + else begin + $write("Error: b = %b, c = %b, d = %b, e = %b ", b, c, d, e); + $write("expected: b = 1, c = 0, d = 0, e = 1\n"); + $stop; + end + end +endmodule From 159cf0429cce19ce85a219bd81370af5ef7ac81c Mon Sep 17 00:00:00 2001 From: Kanad Kanhere <44873394+kkanhere@users.noreply.github.com> Date: Sat, 1 Oct 2022 09:48:37 -0500 Subject: [PATCH 09/12] Support linting for top module interfaces (#3635) --- src/V3Dead.cpp | 48 +++++++- src/V3LinkDot.cpp | 51 +++++++++ src/V3LinkLevel.cpp | 104 ++++++++++++++++++ src/V3LinkParse.cpp | 2 +- src/V3Options.h | 1 + test_regress/t/t_interface_top_bad.pl | 2 +- .../t/t_lint_iface_array_topmodule1.pl | 16 +++ .../t/t_lint_iface_array_topmodule1.v | 48 ++++++++ .../t/t_lint_iface_array_topmodule2.pl | 16 +++ .../t/t_lint_iface_array_topmodule2.v | 39 +++++++ .../t/t_lint_iface_array_topmodule3.pl | 16 +++ .../t/t_lint_iface_array_topmodule3.v | 77 +++++++++++++ .../t/t_lint_iface_array_topmodule_bad.out | 5 + .../t/t_lint_iface_array_topmodule_bad.pl | 19 ++++ .../t/t_lint_iface_array_topmodule_bad.v | 50 +++++++++ test_regress/t/t_lint_iface_topmodule1.pl | 16 +++ test_regress/t/t_lint_iface_topmodule1.v | 42 +++++++ test_regress/t/t_lint_iface_topmodule2.pl | 16 +++ test_regress/t/t_lint_iface_topmodule2.v | 35 ++++++ test_regress/t/t_lint_iface_topmodule3.pl | 16 +++ test_regress/t/t_lint_iface_topmodule3.v | 67 +++++++++++ test_regress/t/t_lint_iface_topmodule_bad.out | 5 + test_regress/t/t_lint_iface_topmodule_bad.pl | 19 ++++ test_regress/t/t_lint_iface_topmodule_bad.v | 44 ++++++++ 24 files changed, 746 insertions(+), 8 deletions(-) create mode 100755 test_regress/t/t_lint_iface_array_topmodule1.pl create mode 100644 test_regress/t/t_lint_iface_array_topmodule1.v create mode 100755 test_regress/t/t_lint_iface_array_topmodule2.pl create mode 100644 test_regress/t/t_lint_iface_array_topmodule2.v create mode 100755 test_regress/t/t_lint_iface_array_topmodule3.pl create mode 100644 test_regress/t/t_lint_iface_array_topmodule3.v create mode 100644 test_regress/t/t_lint_iface_array_topmodule_bad.out create mode 100755 test_regress/t/t_lint_iface_array_topmodule_bad.pl create mode 100644 test_regress/t/t_lint_iface_array_topmodule_bad.v create mode 100755 test_regress/t/t_lint_iface_topmodule1.pl create mode 100644 test_regress/t/t_lint_iface_topmodule1.v create mode 100755 test_regress/t/t_lint_iface_topmodule2.pl create mode 100644 test_regress/t/t_lint_iface_topmodule2.v create mode 100755 test_regress/t/t_lint_iface_topmodule3.pl create mode 100644 test_regress/t/t_lint_iface_topmodule3.v create mode 100644 test_regress/t/t_lint_iface_topmodule_bad.out create mode 100755 test_regress/t/t_lint_iface_topmodule_bad.pl create mode 100644 test_regress/t/t_lint_iface_topmodule_bad.v diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index 83ecf5ffa..f3e8950f8 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -423,10 +423,45 @@ private: } } + void preserveTopIfaces(AstNetlist* rootp) { + for (AstNodeModule* modp = rootp->modulesp(); modp && modp->level() <= 2; + modp = VN_AS(modp->nextp(), NodeModule)) { + for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { + if (AstVar* const varp = VN_CAST(subnodep, Var)) { + if (varp->isIfaceRef()) { + const AstNodeDType* const subtypep = varp->subDTypep(); + const AstIfaceRefDType* ifacerefp = nullptr; + if (VN_IS(subtypep, IfaceRefDType)) { + ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType); + } + else if (VN_IS(subtypep, BracketArrayDType)) { + const AstBracketArrayDType* const arrp = VN_AS(subtypep, BracketArrayDType); + const AstNodeDType* const arrsubtypep = arrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + } + } + else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); + const AstNodeDType* const arrsubtypep = arrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + } + } + + if (ifacerefp && !ifacerefp->cellp() && (ifacerefp->ifacep()->user1() == 0)) { + ifacerefp->ifacep()->user1(1); + } + } + } + } + } + } + public: // CONSTRUCTORS DeadVisitor(AstNetlist* nodep, bool elimUserVars, bool elimDTypes, bool elimScopes, - bool elimCells) + bool elimCells, bool elimTopIfaces) : m_elimUserVars{elimUserVars} , m_elimDTypes{elimDTypes} , m_elimCells{elimCells} { @@ -442,6 +477,7 @@ public: if (elimCells) deadCheckCells(); deadCheckClasses(); // Modules after vars, because might be vars we delete inside a mod we delete + if (!elimTopIfaces) preserveTopIfaces(nodep); deadCheckMod(); // We may have removed some datatypes, cleanup @@ -455,30 +491,30 @@ public: void V3Dead::deadifyModules(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, false, false, false, false}; } // Destruct before checking + { DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6); } void V3Dead::deadifyDTypes(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, false, true, false, false}; } // Destruct before checking + { DeadVisitor{nodep, false, true, false, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypes", 0, dumpTree() >= 3); } void V3Dead::deadifyDTypesScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, false, true, true, false}; } // Destruct before checking + { DeadVisitor{nodep, false, true, true, false, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadDtypesScoped", 0, dumpTree() >= 3); } void V3Dead::deadifyAll(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, true, true, false, true}; } // Destruct before checking + { DeadVisitor{nodep, true, true, false, true, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAll", 0, dumpTree() >= 3); } void V3Dead::deadifyAllScoped(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, true, true, true, true}; } // Destruct before checking + { DeadVisitor{nodep, true, true, true, true, false}; } // Destruct before checking V3Global::dumpCheckGlobalTree("deadAllScoped", 0, dumpTree() >= 3); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 75681218f..f5d93991a 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -308,6 +308,19 @@ public: if (forScopeCreation()) m_nameScopeSymMap.emplace(scopename, symp); return symp; } + VSymEnt* insertTopIface(AstCell* nodep, const string& scopename) { + VSymEnt* const symp = new VSymEnt{&m_syms, nodep}; + UINFO(9, + " INSERTtopiface se" << cvtToHex(symp) << " " << scopename << " " << nodep << endl); + symp->parentp(rootEntp()); // Needed so backward search can find name of top module + symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff + nodep->user1p(symp); + if (nodep->modp()) nodep->modp()->user1p(symp); + checkDuplicate(rootEntp(), nodep, nodep->origName()); + rootEntp()->insert(nodep->origName(), symp); + if (forScopeCreation()) m_nameScopeSymMap.emplace(scopename, symp); + return symp; + } VSymEnt* insertCell(VSymEnt* abovep, VSymEnt* modSymp, AstCell* nodep, const string& scopename) { UASSERT_OBJ(abovep, nodep, "Null symbol table inserting node"); @@ -764,8 +777,46 @@ class LinkDotFindVisitor final : public VNVisitor { modp = VN_AS(modp->nextp(), NodeModule)) { UINFO(8, "Top Module: " << modp << endl); m_scope = "TOP"; + + if (m_statep->forPrearray() && v3Global.opt.topIfacesSupported()) { + for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { + if (AstVar* const varp = VN_CAST(subnodep, Var)) { + if (varp->isIfaceRef()) { + const AstNodeDType* const subtypep = varp->subDTypep(); + const AstIfaceRefDType* ifacerefp = nullptr; + if (VN_IS(subtypep, IfaceRefDType)) { + ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType); + } + else if (VN_IS(subtypep, BracketArrayDType)) { + const AstBracketArrayDType* const arrp = VN_AS(subtypep, BracketArrayDType); + const AstNodeDType* const arrsubtypep = arrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + } + } + else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); + const AstNodeDType* const arrsubtypep = arrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + } + } + + if (ifacerefp && !ifacerefp->cellp()) { + // A dummy cell to keep the top level interface alive and correctly optimized for default parameter values + AstCell* ifacecellp = new AstCell{nodep->fileline(), nodep->fileline(), modp->name() + "__02E" + varp->name(), ifacerefp->ifaceName(), nullptr, nullptr, nullptr}; + ifacecellp->modp(ifacerefp->ifacep()); + m_curSymp = m_modSymp = m_statep->insertTopIface(ifacecellp, m_scope); + { iterate(ifacecellp); } + } + } + } + } + } + m_curSymp = m_modSymp = m_statep->insertTopCell(modp, m_scope); { iterate(modp); } + m_scope = ""; m_curSymp = m_modSymp = nullptr; } diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index cdb9b19c8..fbc4d86bd 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -200,6 +200,35 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { ioNames.insert(oldvarp->name()); } } + else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { + const AstNodeDType* const subtypep = oldvarp->subDTypep(); + if (VN_IS(subtypep, IfaceRefDType)) { + const AstIfaceRefDType* const ifacerefp = VN_AS(subtypep, IfaceRefDType); + if (!ifacerefp->cellp()) { + if (ioNames.find(oldvarp->name()) != ioNames.end()) { + // UINFO(8, "Multitop dup interface found: " << oldvarp << endl); + dupNames.insert(oldvarp->name()); + } else { + ioNames.insert(oldvarp->name()); + } + } + } + if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); + const AstNodeDType* const arrsubtypep = arrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + const AstIfaceRefDType* const ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + if (!ifacerefp->cellp()) { + if (ioNames.find(oldvarp->name()) != ioNames.end()) { + // UINFO(8, "Multitop dup interface array found: " << oldvarp << endl); + dupNames.insert(oldvarp->name()); + } else { + ioNames.insert(oldvarp->name()); + } + } + } + } + } } } } @@ -257,6 +286,81 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { pinp->modVarp(oldvarp); cellp->addPinsp(pinp); } + else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { + // for each interface port on oldmodp instantiate a corresponding interface cell in $root + const AstNodeDType* const subtypep = oldvarp->subDTypep(); + if (VN_IS(subtypep, IfaceRefDType)) { + const AstIfaceRefDType* const ifacerefp = VN_AS(subtypep, IfaceRefDType); + if (!ifacerefp->cellp()) { + string name = oldvarp->name(); + if (dupNames.find(name) != dupNames.end()) { + // __02E=. while __DOT__ looks nicer but will break V3LinkDot + name = oldmodp->name() + "__02E" + name; + } + + AstCell* ifacecellp = new AstCell{newmodp->fileline(), newmodp->fileline(), name, ifacerefp->ifaceName(), nullptr, nullptr, nullptr}; + ifacecellp->modp(ifacerefp->ifacep()); + newmodp->addStmtsp(ifacecellp); + + AstIfaceRefDType* const idtypep = new AstIfaceRefDType{newmodp->fileline(), name, ifacerefp->ifaceName()}; + idtypep->ifacep(nullptr); + idtypep->dtypep(idtypep); + idtypep->cellp(ifacecellp); + rootp->typeTablep()->addTypesp(idtypep); + + AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, name + "__Viftop", idtypep}; + varp->isIfaceParent(true); + ifacecellp->addNextHere(varp); + ifacecellp->hasIfaceVar(true); + + AstPin* const pinp = new AstPin{ + oldvarp->fileline(), 0, varp->name(), + new AstVarRef{varp->fileline(), varp, + oldvarp->isWritable() ? VAccess::WRITE : VAccess::READ}}; + pinp->modVarp(oldvarp); + cellp->addPinsp(pinp); + } + } + else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const oldarrp = VN_AS(subtypep, UnpackArrayDType); + const AstNodeDType* const arrsubtypep = oldarrp->subDTypep(); + if (VN_IS(arrsubtypep, IfaceRefDType)) { + const AstIfaceRefDType* const ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + if (!ifacerefp->cellp()) { + string name = oldvarp->name(); + if (dupNames.find(name) != dupNames.end()) { + // __02E=. while __DOT__ looks nicer but will break V3LinkDot + name = oldmodp->name() + "__02E" + name; + } + + AstUnpackArrayDType* arraydtypep = VN_AS(oldvarp->dtypep(), UnpackArrayDType); + AstCell* ifacearraycellp = new AstCell{newmodp->fileline(), newmodp->fileline(), name, ifacerefp->ifaceName(), nullptr, nullptr, arraydtypep->rangep()->cloneTree(true)}; + ifacearraycellp->modp(ifacerefp->ifacep()); + newmodp->addStmtsp(ifacearraycellp); + + AstIfaceRefDType* const idtypep = new AstIfaceRefDType{newmodp->fileline(), name, ifacerefp->ifaceName()}; + idtypep->ifacep(nullptr); + idtypep->dtypep(idtypep); + idtypep->cellp(ifacearraycellp); + rootp->typeTablep()->addTypesp(idtypep); + + AstNodeArrayDType* const arrp = new AstUnpackArrayDType{newmodp->fileline(), idtypep, arraydtypep->rangep()->cloneTree(true)}; + AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, name + "__Viftop", arrp}; + varp->isIfaceParent(true); + ifacearraycellp->addNextHere(varp); + ifacearraycellp->hasIfaceVar(true); + rootp->typeTablep()->addTypesp(arrp); + + AstPin* const pinp = new AstPin{ + oldvarp->fileline(), 0, varp->name(), + new AstVarRef{varp->fileline(), varp, + oldvarp->isWritable() ? VAccess::WRITE : VAccess::READ}}; + pinp->modVarp(oldvarp); + cellp->addPinsp(pinp); + } + } + } + } } } } diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 43ade69e9..cfeb3d873 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -281,7 +281,7 @@ private: nodep->valuep()->unlinkFrBack())); } } - if (nodep->isIfaceRef() && !nodep->isIfaceParent()) { + if (nodep->isIfaceRef() && !nodep->isIfaceParent() && !v3Global.opt.topIfacesSupported()) { // Only AstIfaceRefDType's at this point correspond to ports; // haven't made additional ones for interconnect yet, so assert is simple // What breaks later is we don't have a Scope/Cell representing diff --git a/src/V3Options.h b/src/V3Options.h index 782987d46..69870a701 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -490,6 +490,7 @@ public: bool vpi() const { return m_vpi; } bool xInitialEdge() const { return m_xInitialEdge; } bool xmlOnly() const { return m_xmlOnly; } + bool topIfacesSupported() const { return lintOnly() && !hierarchical(); } int buildJobs() const { return m_buildJobs; } int convergeLimit() const { return m_convergeLimit; } diff --git a/test_regress/t/t_interface_top_bad.pl b/test_regress/t/t_interface_top_bad.pl index 07964a1b5..8eda3a219 100755 --- a/test_regress/t/t_interface_top_bad.pl +++ b/test_regress/t/t_interface_top_bad.pl @@ -10,7 +10,7 @@ if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); di scenarios(vlt => 1); -lint( +compile( fails => 1, expect_filename => $Self->{golden_filename}, ); diff --git a/test_regress/t/t_lint_iface_array_topmodule1.pl b/test_regress/t/t_lint_iface_array_topmodule1.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule1.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_array_topmodule1.v b/test_regress/t/t_lint_iface_array_topmodule1.v new file mode 100644 index 000000000..de19c13a2 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule1.v @@ -0,0 +1,48 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if; + + logic valid; + logic [7:0] data ; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if [2], + my_if.master_mp out_if [2] + ); + + my_if my_i [2] (); + + always @(posedge clk) + begin + my_i[0].valid <= in_if[0].valid; + my_i[0].data <= in_if[0].data; + + my_i[1].valid <= in_if[1].valid; + my_i[1].data <= in_if[1].data; + end + + assign out_if[0].valid = my_i[0].valid; + assign out_if[0].data = my_i[0].data; + + assign out_if[1].valid = my_i[1].valid; + assign out_if[1].data = my_i[1].data; + +endmodule diff --git a/test_regress/t/t_lint_iface_array_topmodule2.pl b/test_regress/t/t_lint_iface_array_topmodule2.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule2.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_array_topmodule2.v b/test_regress/t/t_lint_iface_array_topmodule2.v new file mode 100644 index 000000000..078fb4495 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule2.v @@ -0,0 +1,39 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( + parameter DW = 8 + ) (); + + logic valid; + logic [DW-1:0] data ; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if [2], + my_if.master_mp out_if [2] + ); + + assign out_if[0].valid = in_if[0].valid; + assign out_if[0].data = in_if[0].data; + + assign out_if[1].valid = in_if[1].valid; + assign out_if[1].data = in_if[1].data; + +endmodule diff --git a/test_regress/t/t_lint_iface_array_topmodule3.pl b/test_regress/t/t_lint_iface_array_topmodule3.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule3.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_array_topmodule3.v b/test_regress/t/t_lint_iface_array_topmodule3.v new file mode 100644 index 000000000..50dae10f2 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule3.v @@ -0,0 +1,77 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( parameter integer DW = 8 ) (input clk); + + localparam DW_LOCAL = DW; + + logic valid; + logic [DW-1:0] data; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + + function automatic integer width(); + return $bits(data); + endfunction + + generate + if (DW < 4) + begin: dw_lt_4_G + function automatic integer min_width(); + return 4; + endfunction + end + else + begin: dw_ge_4_G + function automatic integer min_width(); + return 8; + endfunction + end + endgenerate + +endinterface + +module t + ( + input wire clk, + my_if in_if [2], + my_if out_if [2] + ); + + assign out_if[0].valid = in_if[0].valid; + assign out_if[0].data = in_if[0].data; + + assign out_if[1].valid = in_if[1].valid; + assign out_if[1].data = in_if[1].data; + + my_if my_i (.clk(clk)); + + initial + begin + $display(in_if[0].DW_LOCAL); + $display(in_if[0].width()); + $display(in_if[0].dw_ge_4_G.min_width()); + $display(out_if[0].DW_LOCAL); + $display(out_if[0].width()); + $display(out_if[0].dw_ge_4_G.min_width()); + + $display(in_if[1].DW_LOCAL); + $display(in_if[1].width()); + $display(in_if[1].dw_ge_4_G.min_width()); + $display(out_if[1].DW_LOCAL); + $display(out_if[1].width()); + $display(out_if[1].dw_ge_4_G.min_width()); + end + +endmodule diff --git a/test_regress/t/t_lint_iface_array_topmodule_bad.out b/test_regress/t/t_lint_iface_array_topmodule_bad.out new file mode 100644 index 000000000..6c0980c74 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_lint_iface_array_topmodule_bad.v:8:24: Parameter without initial value is never given value (IEEE 1800-2017 6.20.1): 'DW' + : ... In instance t + 8 | parameter integer DW + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_iface_array_topmodule_bad.pl b/test_regress/t/t_lint_iface_array_topmodule_bad.pl new file mode 100755 index 000000000..a82cf66cb --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_array_topmodule_bad.v b/test_regress/t/t_lint_iface_array_topmodule_bad.v new file mode 100644 index 000000000..55b060591 --- /dev/null +++ b/test_regress/t/t_lint_iface_array_topmodule_bad.v @@ -0,0 +1,50 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( + parameter integer DW + ) (); + + logic valid; + logic [7:0] data ; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if [2], + my_if.master_mp out_if [2] + ); + + my_if my_i [2] (); + + always @(posedge clk) + begin + my_i[0].valid <= in_if[0].valid; + my_i[0].data <= in_if[0].data; + + my_i[1].valid <= in_if[1].valid; + my_i[1].data <= in_if[1].data; + end + + assign out_if[0].valid = my_i[0].valid; + assign out_if[0].data = my_i[0].data; + + assign out_if[1].valid = my_i[1].valid; + assign out_if[1].data = my_i[1].data; + +endmodule diff --git a/test_regress/t/t_lint_iface_topmodule1.pl b/test_regress/t/t_lint_iface_topmodule1.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule1.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_topmodule1.v b/test_regress/t/t_lint_iface_topmodule1.v new file mode 100644 index 000000000..4c4fa5b1f --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule1.v @@ -0,0 +1,42 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if; + + logic valid; + logic [7:0] data ; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if, + my_if.master_mp out_if + ); + + my_if my_i (); + + always @(posedge clk) + begin + my_i.valid <= in_if.valid; + my_i.data <= in_if.data; + end + + assign out_if.valid = my_i.valid; + assign out_if.data = my_i.data; + +endmodule diff --git a/test_regress/t/t_lint_iface_topmodule2.pl b/test_regress/t/t_lint_iface_topmodule2.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule2.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_topmodule2.v b/test_regress/t/t_lint_iface_topmodule2.v new file mode 100644 index 000000000..ec836205a --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule2.v @@ -0,0 +1,35 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( + parameter integer DW = 8 + ) (); + logic valid; + logic [DW-1:0] data; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if, + my_if.master_mp out_if + ); + + assign out_if.valid = in_if.valid; + assign out_if.data = in_if.data; + +endmodule diff --git a/test_regress/t/t_lint_iface_topmodule3.pl b/test_regress/t/t_lint_iface_topmodule3.pl new file mode 100755 index 000000000..7918d5f13 --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule3.pl @@ -0,0 +1,16 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint(); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_topmodule3.v b/test_regress/t/t_lint_iface_topmodule3.v new file mode 100644 index 000000000..a6084207b --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule3.v @@ -0,0 +1,67 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( parameter integer DW = 8 ) (input clk); + + localparam DW_LOCAL = DW; + + logic valid; + logic [DW-1:0] data; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + + function automatic integer width(); + return $bits(data); + endfunction + + generate + if (DW < 4) + begin: dw_lt_4_G + function automatic integer min_width(); + return 4; + endfunction + end + else + begin: dw_ge_4_G + function automatic integer min_width(); + return 8; + endfunction + end + endgenerate + +endinterface + +module t + ( + input wire clk, + my_if in_if, + my_if out_if + ); + + assign out_if.valid = in_if.valid; + assign out_if.data = in_if.data; + + my_if my_i (.clk(clk)); + + initial + begin + $display(in_if.DW_LOCAL); + $display(in_if.width()); + $display(in_if.dw_ge_4_G.min_width()); + $display(out_if.DW_LOCAL); + $display(out_if.width()); + $display(out_if.dw_ge_4_G.min_width()); + end + +endmodule diff --git a/test_regress/t/t_lint_iface_topmodule_bad.out b/test_regress/t/t_lint_iface_topmodule_bad.out new file mode 100644 index 000000000..b5263bb79 --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule_bad.out @@ -0,0 +1,5 @@ +%Error: t/t_lint_iface_topmodule_bad.v:8:23: Parameter without initial value is never given value (IEEE 1800-2017 6.20.1): 'DW' + : ... In instance t + 8 | parameter integer DW + | ^~ +%Error: Exiting due to diff --git a/test_regress/t/t_lint_iface_topmodule_bad.pl b/test_regress/t/t_lint_iface_topmodule_bad.pl new file mode 100755 index 000000000..a82cf66cb --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule_bad.pl @@ -0,0 +1,19 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2008 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 + +scenarios(linter => 1); + +lint( + fails => 1, + expect_filename => $Self->{golden_filename}, + ); + +ok(1); +1; diff --git a/test_regress/t/t_lint_iface_topmodule_bad.v b/test_regress/t/t_lint_iface_topmodule_bad.v new file mode 100644 index 000000000..04e51f7c3 --- /dev/null +++ b/test_regress/t/t_lint_iface_topmodule_bad.v @@ -0,0 +1,44 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2017 by Josh Redford. +// SPDX-License-Identifier: CC0-1.0 + +interface my_if #( + parameter integer DW + ) (); + + logic valid; + logic [DW-1:0] data ; + + modport slave_mp ( + input valid, + input data + ); + + modport master_mp ( + output valid, + output data + ); + +endinterface + +module t + ( + input wire clk, + my_if.slave_mp in_if, + my_if.master_mp out_if + ); + + my_if my_i (); + + always @(posedge clk) + begin + my_i.valid <= in_if.valid; + my_i.data <= in_if.data; + end + + assign out_if.valid = my_i.valid; + assign out_if.data = my_i.data; + +endmodule From f1ba6cb517d3aa74d37837bffe8eefa7e5307518 Mon Sep 17 00:00:00 2001 From: github action Date: Sat, 1 Oct 2022 14:53:40 +0000 Subject: [PATCH 10/12] Apply 'make format' --- src/V3Dead.cpp | 21 +++++++------ src/V3LinkDot.cpp | 31 ++++++++++++------- src/V3LinkLevel.cpp | 72 ++++++++++++++++++++++++++++++--------------- 3 files changed, 80 insertions(+), 44 deletions(-) diff --git a/src/V3Dead.cpp b/src/V3Dead.cpp index f3e8950f8..38800e6f9 100644 --- a/src/V3Dead.cpp +++ b/src/V3Dead.cpp @@ -425,7 +425,7 @@ private: void preserveTopIfaces(AstNetlist* rootp) { for (AstNodeModule* modp = rootp->modulesp(); modp && modp->level() <= 2; - modp = VN_AS(modp->nextp(), NodeModule)) { + modp = VN_AS(modp->nextp(), NodeModule)) { for (AstNode* subnodep = modp->stmtsp(); subnodep; subnodep = subnodep->nextp()) { if (AstVar* const varp = VN_CAST(subnodep, Var)) { if (varp->isIfaceRef()) { @@ -433,23 +433,24 @@ private: const AstIfaceRefDType* ifacerefp = nullptr; if (VN_IS(subtypep, IfaceRefDType)) { ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType); - } - else if (VN_IS(subtypep, BracketArrayDType)) { - const AstBracketArrayDType* const arrp = VN_AS(subtypep, BracketArrayDType); + } else if (VN_IS(subtypep, BracketArrayDType)) { + const AstBracketArrayDType* const arrp + = VN_AS(subtypep, BracketArrayDType); const AstNodeDType* const arrsubtypep = arrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); } - } - else if (VN_IS(subtypep, UnpackArrayDType)) { - const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); + } else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const arrp + = VN_AS(subtypep, UnpackArrayDType); const AstNodeDType* const arrsubtypep = arrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); } } - if (ifacerefp && !ifacerefp->cellp() && (ifacerefp->ifacep()->user1() == 0)) { + if (ifacerefp && !ifacerefp->cellp() + && (ifacerefp->ifacep()->user1() == 0)) { ifacerefp->ifacep()->user1(1); } } @@ -491,7 +492,9 @@ public: void V3Dead::deadifyModules(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()}; } // Destruct before checking + { + DeadVisitor{nodep, false, false, false, false, !v3Global.opt.topIfacesSupported()}; + } // Destruct before checking V3Global::dumpCheckGlobalTree("deadModules", 0, dumpTree() >= 6); } diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index f5d93991a..0611e6297 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -310,8 +310,8 @@ public: } VSymEnt* insertTopIface(AstCell* nodep, const string& scopename) { VSymEnt* const symp = new VSymEnt{&m_syms, nodep}; - UINFO(9, - " INSERTtopiface se" << cvtToHex(symp) << " " << scopename << " " << nodep << endl); + UINFO(9, " INSERTtopiface se" << cvtToHex(symp) << " " << scopename << " " << nodep + << endl); symp->parentp(rootEntp()); // Needed so backward search can find name of top module symp->fallbackp(dunitEntp()); // Needed so can find $unit stuff nodep->user1p(symp); @@ -786,16 +786,16 @@ class LinkDotFindVisitor final : public VNVisitor { const AstIfaceRefDType* ifacerefp = nullptr; if (VN_IS(subtypep, IfaceRefDType)) { ifacerefp = VN_AS(varp->subDTypep(), IfaceRefDType); - } - else if (VN_IS(subtypep, BracketArrayDType)) { - const AstBracketArrayDType* const arrp = VN_AS(subtypep, BracketArrayDType); + } else if (VN_IS(subtypep, BracketArrayDType)) { + const AstBracketArrayDType* const arrp + = VN_AS(subtypep, BracketArrayDType); const AstNodeDType* const arrsubtypep = arrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); } - } - else if (VN_IS(subtypep, UnpackArrayDType)) { - const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); + } else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const arrp + = VN_AS(subtypep, UnpackArrayDType); const AstNodeDType* const arrsubtypep = arrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); @@ -803,10 +803,19 @@ class LinkDotFindVisitor final : public VNVisitor { } if (ifacerefp && !ifacerefp->cellp()) { - // A dummy cell to keep the top level interface alive and correctly optimized for default parameter values - AstCell* ifacecellp = new AstCell{nodep->fileline(), nodep->fileline(), modp->name() + "__02E" + varp->name(), ifacerefp->ifaceName(), nullptr, nullptr, nullptr}; + // A dummy cell to keep the top level interface alive and correctly + // optimized for default parameter values + AstCell* ifacecellp + = new AstCell{nodep->fileline(), + nodep->fileline(), + modp->name() + "__02E" + varp->name(), + ifacerefp->ifaceName(), + nullptr, + nullptr, + nullptr}; ifacecellp->modp(ifacerefp->ifacep()); - m_curSymp = m_modSymp = m_statep->insertTopIface(ifacecellp, m_scope); + m_curSymp = m_modSymp + = m_statep->insertTopIface(ifacecellp, m_scope); { iterate(ifacecellp); } } } diff --git a/src/V3LinkLevel.cpp b/src/V3LinkLevel.cpp index fbc4d86bd..417cd08dc 100644 --- a/src/V3LinkLevel.cpp +++ b/src/V3LinkLevel.cpp @@ -199,8 +199,7 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { } else { ioNames.insert(oldvarp->name()); } - } - else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { + } else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { const AstNodeDType* const subtypep = oldvarp->subDTypep(); if (VN_IS(subtypep, IfaceRefDType)) { const AstIfaceRefDType* const ifacerefp = VN_AS(subtypep, IfaceRefDType); @@ -217,10 +216,12 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { const AstUnpackArrayDType* const arrp = VN_AS(subtypep, UnpackArrayDType); const AstNodeDType* const arrsubtypep = arrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { - const AstIfaceRefDType* const ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + const AstIfaceRefDType* const ifacerefp + = VN_AS(arrsubtypep, IfaceRefDType); if (!ifacerefp->cellp()) { if (ioNames.find(oldvarp->name()) != ioNames.end()) { - // UINFO(8, "Multitop dup interface array found: " << oldvarp << endl); + // UINFO(8, "Multitop dup interface array found: " << oldvarp + // << endl); dupNames.insert(oldvarp->name()); } else { ioNames.insert(oldvarp->name()); @@ -285,9 +286,9 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { // Skip length and width comp; we know it's a direct assignment pinp->modVarp(oldvarp); cellp->addPinsp(pinp); - } - else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { - // for each interface port on oldmodp instantiate a corresponding interface cell in $root + } else if (v3Global.opt.topIfacesSupported() && oldvarp->isIfaceRef()) { + // for each interface port on oldmodp instantiate a corresponding interface + // cell in $root const AstNodeDType* const subtypep = oldvarp->subDTypep(); if (VN_IS(subtypep, IfaceRefDType)) { const AstIfaceRefDType* const ifacerefp = VN_AS(subtypep, IfaceRefDType); @@ -298,34 +299,44 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { name = oldmodp->name() + "__02E" + name; } - AstCell* ifacecellp = new AstCell{newmodp->fileline(), newmodp->fileline(), name, ifacerefp->ifaceName(), nullptr, nullptr, nullptr}; + AstCell* ifacecellp = new AstCell{newmodp->fileline(), + newmodp->fileline(), + name, + ifacerefp->ifaceName(), + nullptr, + nullptr, + nullptr}; ifacecellp->modp(ifacerefp->ifacep()); newmodp->addStmtsp(ifacecellp); - AstIfaceRefDType* const idtypep = new AstIfaceRefDType{newmodp->fileline(), name, ifacerefp->ifaceName()}; + AstIfaceRefDType* const idtypep = new AstIfaceRefDType{ + newmodp->fileline(), name, ifacerefp->ifaceName()}; idtypep->ifacep(nullptr); idtypep->dtypep(idtypep); idtypep->cellp(ifacecellp); rootp->typeTablep()->addTypesp(idtypep); - AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, name + "__Viftop", idtypep}; + AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, + name + "__Viftop", idtypep}; varp->isIfaceParent(true); ifacecellp->addNextHere(varp); ifacecellp->hasIfaceVar(true); - AstPin* const pinp = new AstPin{ - oldvarp->fileline(), 0, varp->name(), - new AstVarRef{varp->fileline(), varp, - oldvarp->isWritable() ? VAccess::WRITE : VAccess::READ}}; + AstPin* const pinp + = new AstPin{oldvarp->fileline(), 0, varp->name(), + new AstVarRef{varp->fileline(), varp, + oldvarp->isWritable() ? VAccess::WRITE + : VAccess::READ}}; pinp->modVarp(oldvarp); cellp->addPinsp(pinp); } - } - else if (VN_IS(subtypep, UnpackArrayDType)) { - const AstUnpackArrayDType* const oldarrp = VN_AS(subtypep, UnpackArrayDType); + } else if (VN_IS(subtypep, UnpackArrayDType)) { + const AstUnpackArrayDType* const oldarrp + = VN_AS(subtypep, UnpackArrayDType); const AstNodeDType* const arrsubtypep = oldarrp->subDTypep(); if (VN_IS(arrsubtypep, IfaceRefDType)) { - const AstIfaceRefDType* const ifacerefp = VN_AS(arrsubtypep, IfaceRefDType); + const AstIfaceRefDType* const ifacerefp + = VN_AS(arrsubtypep, IfaceRefDType); if (!ifacerefp->cellp()) { string name = oldvarp->name(); if (dupNames.find(name) != dupNames.end()) { @@ -333,19 +344,31 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { name = oldmodp->name() + "__02E" + name; } - AstUnpackArrayDType* arraydtypep = VN_AS(oldvarp->dtypep(), UnpackArrayDType); - AstCell* ifacearraycellp = new AstCell{newmodp->fileline(), newmodp->fileline(), name, ifacerefp->ifaceName(), nullptr, nullptr, arraydtypep->rangep()->cloneTree(true)}; + AstUnpackArrayDType* arraydtypep + = VN_AS(oldvarp->dtypep(), UnpackArrayDType); + AstCell* ifacearraycellp + = new AstCell{newmodp->fileline(), + newmodp->fileline(), + name, + ifacerefp->ifaceName(), + nullptr, + nullptr, + arraydtypep->rangep()->cloneTree(true)}; ifacearraycellp->modp(ifacerefp->ifacep()); newmodp->addStmtsp(ifacearraycellp); - AstIfaceRefDType* const idtypep = new AstIfaceRefDType{newmodp->fileline(), name, ifacerefp->ifaceName()}; + AstIfaceRefDType* const idtypep = new AstIfaceRefDType{ + newmodp->fileline(), name, ifacerefp->ifaceName()}; idtypep->ifacep(nullptr); idtypep->dtypep(idtypep); idtypep->cellp(ifacearraycellp); rootp->typeTablep()->addTypesp(idtypep); - AstNodeArrayDType* const arrp = new AstUnpackArrayDType{newmodp->fileline(), idtypep, arraydtypep->rangep()->cloneTree(true)}; - AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, name + "__Viftop", arrp}; + AstNodeArrayDType* const arrp = new AstUnpackArrayDType{ + newmodp->fileline(), idtypep, + arraydtypep->rangep()->cloneTree(true)}; + AstVar* varp = new AstVar{newmodp->fileline(), VVarType::IFACEREF, + name + "__Viftop", arrp}; varp->isIfaceParent(true); ifacearraycellp->addNextHere(varp); ifacearraycellp->hasIfaceVar(true); @@ -354,7 +377,8 @@ void V3LinkLevel::wrapTopCell(AstNetlist* rootp) { AstPin* const pinp = new AstPin{ oldvarp->fileline(), 0, varp->name(), new AstVarRef{varp->fileline(), varp, - oldvarp->isWritable() ? VAccess::WRITE : VAccess::READ}}; + oldvarp->isWritable() ? VAccess::WRITE + : VAccess::READ}}; pinp->modVarp(oldvarp); cellp->addPinsp(pinp); } From 526e6b9fc7142aa5dcf9f0cab0babdf41afbea46 Mon Sep 17 00:00:00 2001 From: Marcel Chang <112921644+marcelchangTW@users.noreply.github.com> Date: Sat, 1 Oct 2022 23:05:33 +0800 Subject: [PATCH 11/12] Add --dump-tree-dot to enable dumping Ast Tree .dot files (#3636) --- docs/CONTRIBUTORS | 1 + docs/guide/exe_verilator.rst | 6 +++++ docs/internals.rst | 10 ++++++++ src/V3Ast.cpp | 40 +++++++++++++++++++++++++++++++ src/V3Ast.h | 2 ++ src/V3Global.cpp | 7 ++++-- src/V3Options.cpp | 5 ++++ src/V3Options.h | 1 + test_regress/t/t_dump_tree_dot.pl | 20 ++++++++++++++++ 9 files changed, 90 insertions(+), 2 deletions(-) create mode 100755 test_regress/t/t_dump_tree_dot.pl diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index 0c3be357f..df73f955e 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -71,6 +71,7 @@ Ludwig Rogiers Lukasz Dalek Maarten De Braekeleer Maciej Sobkowski +Marcel Chang Marco Widmer Mariusz Glebocki Markus Krause diff --git a/docs/guide/exe_verilator.rst b/docs/guide/exe_verilator.rst index a54510df4..b800261a2 100644 --- a/docs/guide/exe_verilator.rst +++ b/docs/guide/exe_verilator.rst @@ -373,6 +373,12 @@ Summary: <--dump-tree>` may be useful if the dump files are large and not desired. +.. option:: --dump-tree-dot + + Rarely needed. Enable dumping Ast .tree.dot debug files in Graphviz + Dot format. This option implies :vlopt:`--dump-tree`, unless + :vlopt:`--dumpi-tree` was passed explicitly. + .. option:: --dump-tree-addrids Rarely needed - for developer use. Replace AST node addresses with diff --git a/docs/internals.rst b/docs/internals.rst index c62881026..40cc6b3aa 100644 --- a/docs/internals.rst +++ b/docs/internals.rst @@ -1137,6 +1137,16 @@ Similarly the ``NETLIST`` has a list of modules referred to by its ``op1p()`` pointer. +.tree.dot Output +---------------- + +``*.tree.dot`` files are dumps of the AST Tree in `Graphviz +`__ dot format. This can be used to +visualize the AST Tree. The vertices correspond to ``AstNode`` +instances, and the edges represent the pointers (``op1p``, + ``op2p``, etc) between the nodes. + + Debugging with GDB ------------------ diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index 8f7b102ef..c83af8820 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1141,6 +1141,46 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo } } +static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp, const std::string& childName) { + if (childp) { + os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " [" + << "label=\"" << childName << "\" color=red];\n"; + for (const AstNode* nodep = childp; nodep; nodep = nodep->nextp()) { + nodep->dumpTreeDot(os); + if (nodep->nextp()) { + os << "\tn" << cvtToHex(nodep) << " -> n" << cvtToHex(nodep->nextp()) << " [" + << "label=\"next\" color=red];\n"; + os << "\t{rank=same; n" << cvtToHex(nodep) << ", n" << cvtToHex(nodep->nextp()) + << "}\n"; + } + } + } +} + +void AstNode::dumpTreeDot(std::ostream& os) const { + os << "\tn" << cvtToHex(this) << "\t[" + << "label=\"" << typeName() << "\\n" < treedotp{V3File::new_ofstream(filename, append)}; + if (treedotp->fail()) v3fatal("Can't write " << filename); + *treedotp << "digraph vTree{\n"; + *treedotp << "\tgraph\t[label=\"" << filename + ".dot" << "\",\n"; + *treedotp << "\t\t labelloc=t, labeljust=l,\n"; + *treedotp << "\t\t //size=\"7.5,10\",\n" << "];\n"; + dumpTreeDot(*treedotp); + *treedotp << "}\n"; + } +} + void AstNode::v3errorEndFatal(std::ostringstream& str) const { v3errorEnd(str); assert(0); // LCOV_EXCL_LINE diff --git a/src/V3Ast.h b/src/V3Ast.h index 01a6af34c..b00436897 100644 --- a/src/V3Ast.h +++ b/src/V3Ast.h @@ -1862,6 +1862,8 @@ public: void dumpTreeFile(const string& filename, bool append = false, bool doDump = true, bool doCheck = true); static void dumpTreeFileGdb(const AstNode* nodep, const char* filenamep = nullptr); + void dumpTreeDot(std::ostream& os = std::cout) const; + void dumpTreeDotFile(const string& filename, bool append = false, bool doDump = true); // METHODS - queries // Changes control flow, disable some optimizations diff --git a/src/V3Global.cpp b/src/V3Global.cpp index 5510386f0..34cbe343b 100644 --- a/src/V3Global.cpp +++ b/src/V3Global.cpp @@ -93,8 +93,11 @@ string V3Global::digitsFilename(int number) { } void V3Global::dumpCheckGlobalTree(const string& stagename, int newNumber, bool doDump) { - v3Global.rootp()->dumpTreeFile(v3Global.debugFilename(stagename + ".tree", newNumber), false, - doDump); + const string treeFilename = v3Global.debugFilename(stagename + ".tree", newNumber); + v3Global.rootp()->dumpTreeFile(treeFilename, false, doDump); + if (v3Global.opt.dumpTreeDot()) { + v3Global.rootp()->dumpTreeDotFile(treeFilename + ".dot", false, doDump); + } if (v3Global.opt.stats()) V3Stats::statsStage(stagename); } diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 4456f601e..235cef99f 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -820,6 +820,11 @@ void V3Options::notify() { // 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"]; + } } //###################################################################### diff --git a/src/V3Options.h b/src/V3Options.h index 69870a701..4acf71308 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -454,6 +454,7 @@ public: bool decoration() const { return m_decoration; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); } + bool dumpTreeDot() const { return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); } bool exe() const { return m_exe; } bool flatten() const { return m_flatten; } bool gmake() const { return m_gmake; } diff --git a/test_regress/t/t_dump_tree_dot.pl b/test_regress/t/t_dump_tree_dot.pl new file mode 100755 index 000000000..8caebed51 --- /dev/null +++ b/test_regress/t/t_dump_tree_dot.pl @@ -0,0 +1,20 @@ +#!/usr/bin/env perl +if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; } +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2003 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 + +scenarios(vlt => 1); + +top_filename("t/t_EXAMPLE.v"); + +lint( + v_flags => ["--lint-only --dump-tree-dot"], + ); + +ok(1); +1; From a204b24fcfd3f78035f6b7d81646d4bd209fe29a Mon Sep 17 00:00:00 2001 From: github action Date: Sat, 1 Oct 2022 15:06:12 +0000 Subject: [PATCH 12/12] Apply 'make format' --- src/V3Ast.cpp | 13 ++++++++----- src/V3Options.h | 4 +++- 2 files changed, 11 insertions(+), 6 deletions(-) diff --git a/src/V3Ast.cpp b/src/V3Ast.cpp index c83af8820..1bd1ab6b9 100644 --- a/src/V3Ast.cpp +++ b/src/V3Ast.cpp @@ -1141,7 +1141,8 @@ void AstNode::dumpTreeFile(const string& filename, bool append, bool doDump, boo } } -static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp, const std::string& childName) { +static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* childp, + const std::string& childName) { if (childp) { os << "\tn" << cvtToHex(thisp) << " -> n" << cvtToHex(childp) << " [" << "label=\"" << childName << "\" color=red];\n"; @@ -1159,8 +1160,8 @@ static void drawChildren(std::ostream& os, const AstNode* thisp, const AstNode* void AstNode::dumpTreeDot(std::ostream& os) const { os << "\tn" << cvtToHex(this) << "\t[" - << "label=\"" << typeName() << "\\n" < treedotp{V3File::new_ofstream(filename, append)}; if (treedotp->fail()) v3fatal("Can't write " << filename); *treedotp << "digraph vTree{\n"; - *treedotp << "\tgraph\t[label=\"" << filename + ".dot" << "\",\n"; + *treedotp << "\tgraph\t[label=\"" << filename + ".dot" + << "\",\n"; *treedotp << "\t\t labelloc=t, labeljust=l,\n"; - *treedotp << "\t\t //size=\"7.5,10\",\n" << "];\n"; + *treedotp << "\t\t //size=\"7.5,10\",\n" + << "];\n"; dumpTreeDot(*treedotp); *treedotp << "}\n"; } diff --git a/src/V3Options.h b/src/V3Options.h index 4acf71308..9f1bd687c 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -454,7 +454,9 @@ public: bool decoration() const { return m_decoration; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); } - bool dumpTreeDot() const { return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); } + bool dumpTreeDot() const { + return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); + } bool exe() const { return m_exe; } bool flatten() const { return m_flatten; } bool gmake() const { return m_gmake; }