diff --git a/Changes b/Changes index fc5ef95f2..e3d2df32e 100644 --- a/Changes +++ b/Changes @@ -20,6 +20,7 @@ Verilator 5.035 devel **Other:** * Support force/release with a variable reference (#5721) (#5810). [Bartłomiej Chmiel, Antmicro Ltd.] +* Support `systemc_interface and related inside `class`. * Add multi-thread hierarchical simulation (#2583) (#5871). [Bartłomiej Chmiel, Antmicro Ltd.] * Add check for `let` misused in statement context (#5733). * Add used language to `--preproc-resolve` output (#5795). [Kamil Rakoczy, Antmicro Ltd.] diff --git a/docs/guide/extensions.rst b/docs/guide/extensions.rst index e797ccc50..ca6a36ddc 100644 --- a/docs/guide/extensions.rst +++ b/docs/guide/extensions.rst @@ -107,48 +107,60 @@ or "`ifdef`"'s may break other tools. Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it verbatim into the output .h file's header. Must be placed as a module - item, e.g., directly inside a module/endmodule pair. Despite the name of - this macro, this also works in pure C++ code. + or class item, e.g., directly inside a module/endmodule or + class/endclass pair. Despite the name of this macro, this also works in + pure C++ code. + +.. option:: `systemc_class_name + + Inside one of the :option:`\`systemc_... <\`systemc_header>` text + blocks, replaced with the C++ class name generated for the given + containing SystemVerilog class or module. Currently this is replaced + blindly, ignoring quoting or other escapes; this behavior may change in + the future. This attribute is indented only to be used internally in + `verilated_std.sv`. .. option:: `systemc_ctor Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it - verbatim into the C++ class constructor. Must be placed as a module - item, e.g., directly inside a module/endmodule pair. Despite the name of - this macro, this also works in pure C++ code. + verbatim into the C++ class constructor. Must be placed as a module or + class item, e.g., directly inside a module/endmodule or class/endclass + pair. Despite the name of this macro, this also works in pure C++ code. .. option:: `systemc_dtor Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it - verbatim into the C++ class destructor. Must be placed as a module - item, e.g., directly inside a module/endmodule pair. Despite the name of - this macro, this also works in pure C++ code. + verbatim into the C++ class destructor. Must be placed as a module or + class item, e.g., directly inside a module/endmodule or class/endclass + pair. Despite the name of this macro, this also works in pure C++ code. .. option:: `systemc_interface Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it - verbatim into the C++ class interface. Must be placed as a module item, - e.g., directly inside a module/endmodule pair. Despite the name of this - macro, this also works in pure C++ code. + verbatim into the C++ class interface. Must be placed as a module or + class item, e.g., directly inside a module/endmodule or class/endclass + pair. Despite the name of this macro, this also works in pure C++ code. .. option:: `systemc_imp_header Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it verbatim into the header of all files for this C++ class implementation. - Must be placed as a module item, e.g., directly inside a module/endmodule - pair. Despite the name of this macro, this also works in pure C++ code. + Must be placed as a module or class item, e.g., directly inside a + module/endmodule or class/endclass pair. Despite the name of this macro, + this also works in pure C++ code. .. option:: `systemc_implementation Take the remaining text up to the next :option:`\`verilog` or :option:`\`systemc_... <\`systemc_header>` mode switch and place it verbatim into a single file of the C++ class implementation. Must be - placed as a module item, e.g., directly inside a module/endmodule - pair. Despite the name of this macro, this also works in pure C++ code. + placed as a module or class item, e.g., directly inside a + module/endmodule or class/endclass pair. Despite the name of this macro, + this also works in pure C++ code. If you will be reading or writing any Verilog variables in the C++ functions, the Verilog signals must be declared with a diff --git a/src/V3CCtors.cpp b/src/V3CCtors.cpp index 2d0c1305d..566846f17 100644 --- a/src/V3CCtors.cpp +++ b/src/V3CCtors.cpp @@ -137,7 +137,17 @@ class CCtorsVisitor final : public VNVisitor { AstCFunc* m_cfuncp = nullptr; // Current function V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset - // VISITs + // METHODS + static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VNType type) { + auto textAndFileline = EmitCBaseVisitorConst::textSection(modp, type); + if (!textAndFileline.first.empty()) { + AstTextBlock* const newp + = new AstTextBlock{textAndFileline.second, textAndFileline.first, false, false}; + cfuncp->addStmtsp(newp); + } + } + + // VISITORS void visit(AstNodeModule* nodep) override { VL_RESTORER(m_modp); VL_RESTORER(m_varResetp); @@ -167,6 +177,7 @@ class CCtorsVisitor final : public VNVisitor { // If can be referred to by base pointer, need virtual delete funcp->isVirtual(classp->isExtended()); funcp->slow(false); + insertSc(funcp, classp, VNType::atScDtor); classp->addStmtsp(funcp); } } @@ -177,6 +188,7 @@ class CCtorsVisitor final : public VNVisitor { m_varResetp = nullptr; m_cfuncp = nodep; iterateChildren(nodep); + if (nodep->name() == "new") insertSc(nodep, m_modp, VNType::atScCtor); } void visit(AstVar* nodep) override { if (nodep->needsCReset()) { diff --git a/src/V3EmitCBase.cpp b/src/V3EmitCBase.cpp index 2cb45c20b..5f8b81e66 100644 --- a/src/V3EmitCBase.cpp +++ b/src/V3EmitCBase.cpp @@ -285,27 +285,43 @@ void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useT if (nl) puts("\n"); } +std::pair EmitCBaseVisitorConst::textSection(const AstNodeModule* modp, + VNType type) { + if (!v3Global.hasSCTextSections()) return std::make_pair("", nullptr); + string text; + FileLine* fl = nullptr; + int last_line = -999; + for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { + if (nodep->type() != type) continue; + if (const AstNodeText* const textp = VN_CAST(nodep, NodeText)) { + if (text.empty()) { + fl = textp->fileline(); + text += "\n"; + if (v3Global.opt.decoration()) + text += "\n//*** Below code from `systemc in Verilog file\n"; + } + if (last_line + 1 != nodep->fileline()->lineno() && v3Global.opt.decoration()) + text += "// From `systemc at " + nodep->fileline()->ascii() + "\n"; + last_line = textp->fileline()->lineno(); + text += textp->text(); + } + } + if (!text.empty()) { + if (v3Global.opt.decoration()) text += "//*** Above code from `systemc in Verilog file\n"; + text += "\n"; + // Substitute `systemc_class_name + string::size_type pos; + while ((pos = text.find("`systemc_class_name")) != string::npos) { + text.replace(pos, std::strlen("`systemc_class_name"), + EmitCBase::prefixNameProtect(modp)); + } + } + return std::make_pair(text, fl); +} + void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) { // Short circuit if nothing to do. This can save a lot of time on large designs as this // function needs to traverse the entire module linearly. - if (!v3Global.hasSCTextSections()) return; - - int last_line = -999; - for (AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) { - if (const AstNodeText* const textp = VN_CAST(nodep, NodeText)) { - if (nodep->type() == type) { - if (last_line != nodep->fileline()->lineno()) { - if (last_line < 0) { - putns(nodep, "\n//*** Below code from `systemc in Verilog file\n"); - } - putsDecoration(nodep, ifNoProtect("// From `systemc at " - + nodep->fileline()->ascii() + "\n")); - last_line = nodep->fileline()->lineno(); - } - ofp()->putsNoTracking(textp->text()); - last_line++; - } - } - } - if (last_line > 0) puts("//*** Above code from `systemc in Verilog file\n\n"); + auto textAndFileline = textSection(modp, type); + if (!textAndFileline.first.empty()) ofp()->putsNoTracking(textAndFileline.first); } diff --git a/src/V3EmitCBase.h b/src/V3EmitCBase.h index ff2305aaf..c6dce2755 100644 --- a/src/V3EmitCBase.h +++ b/src/V3EmitCBase.h @@ -164,6 +164,7 @@ public: } void emitModCUse(const AstNodeModule* modp, VUseType useType); void emitTextSection(const AstNodeModule* modp, VNType type); + static std::pair textSection(const AstNodeModule* modp, VNType type); // CONSTRUCTORS EmitCBaseVisitorConst() = default; diff --git a/src/V3LinkCells.cpp b/src/V3LinkCells.cpp index 85ccb83b1..3bd67bdef 100644 --- a/src/V3LinkCells.cpp +++ b/src/V3LinkCells.cpp @@ -162,7 +162,7 @@ class LinkCellsVisitor final : public VNVisitor { return modp; } - // VISITs + // VISITORS void visit(AstNetlist* nodep) override { readModNames(); iterateChildren(nodep); diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index 86413d918..acd2fa9b2 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -846,7 +846,7 @@ class LinkDotFindVisitor final : public VNVisitor { return hierBlocks.find(name) != hierBlocks.end(); } - // VISITs + // VISITORS void visit(AstNetlist* nodep) override { // Process $unit or other packages // Not needed - dotted references not allowed from inside packages @@ -1791,7 +1791,7 @@ class LinkDotParamVisitor final : public VNVisitor { } } - // VISITs + // VISITORS void visit(AstTypeTable*) override {} void visit(AstConstPool*) override {} void visit(AstNodeModule* nodep) override { @@ -1967,7 +1967,7 @@ class LinkDotScopeVisitor final : public VNVisitor { const AstScope* m_scopep = nullptr; // The current scope VSymEnt* m_modSymp = nullptr; // Symbol entry for current module - // VISITs + // VISITORS void visit(AstNetlist* nodep) override { // Recurse..., backward as must do packages before using packages iterateChildrenBackwardsConst(nodep); @@ -2131,7 +2131,7 @@ class LinkDotIfaceVisitor final : public VNVisitor { LinkDotState* const m_statep; // State to pass between visitors, including symbol table VSymEnt* m_curSymp; // Symbol Entry for current table, where to lookup/insert - // VISITs + // VISITORS void visit(AstModport* nodep) override { // Modport: Remember its name for later resolution UINFO(5, " fiv: " << nodep << endl); @@ -2543,7 +2543,7 @@ class LinkDotResolveVisitor final : public VNVisitor { return result + " "; } - // VISITs + // VISITORS void visit(AstNetlist* nodep) override { // Recurse..., backward as must do packages before using packages iterateChildrenBackwardsConst(nodep); diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index 7bdd6b9b5..b119550f7 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -41,7 +41,7 @@ class LinkLValueVisitor final : public VNVisitor { bool m_inInitialStatic = false; // Set if inside AstInitialStatic VAccess m_setRefLvalue; // Set VarRefs to lvalues for pin assignments - // VISITs + // VISITORS // Result handing void visit(AstNodeVarRef* nodep) override { // VarRef: LValue its reference diff --git a/src/V3LinkParse.cpp b/src/V3LinkParse.cpp index 6ba1c8737..d2860ff58 100644 --- a/src/V3LinkParse.cpp +++ b/src/V3LinkParse.cpp @@ -175,7 +175,7 @@ class LinkParseVisitor final : public VNVisitor { << nodep->warnContextSecondary()); } - // VISITs + // VISITORS void visit(AstNodeFTask* nodep) override { if (!nodep->user1SetOnce()) { // Process only once. // Mark class methods diff --git a/src/V3LinkResolve.cpp b/src/V3LinkResolve.cpp index 6a3fb567b..cf3572720 100644 --- a/src/V3LinkResolve.cpp +++ b/src/V3LinkResolve.cpp @@ -55,7 +55,7 @@ class LinkResolveVisitor final : public VNVisitor { bool m_underGenFor = false; // Under GenFor bool m_underGenerate = false; // Under GenFor/GenIf - // VISITs + // VISITORS // TODO: Most of these visitors are here for historical reasons. // TODO: ExpectDescriptor can move to data type resolution, and the rest // TODO: could move to V3LinkParse to get them out of the way of elaboration @@ -531,7 +531,7 @@ class LinkBotupVisitor final : public VNVisitorConst { // STATE AstNodeModule* m_modp = nullptr; // Current module - // VISITs + // VISITORS void visit(AstNetlist* nodep) override { // Iterate modules backwards, in bottom-up order. iterateChildrenBackwardsConst(nodep); diff --git a/src/verilog.y b/src/verilog.y index 9285444d9..5885da7f0 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -2729,18 +2729,23 @@ non_port_module_item: // ==IEEE: non_port_module_item { $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); } | timeunits_declaration { $$ = $1; } // // Verilator specific - | yaSCHDR { $$ = new AstScHdr{$1, *$1}; v3Global.setHasSCTextSections(); } - | yaSCINT { $$ = new AstScInt{$1, *$1}; v3Global.setHasSCTextSections(); } - | yaSCIMP { $$ = new AstScImp{$1, *$1}; v3Global.setHasSCTextSections(); } - | yaSCIMPH { $$ = new AstScImpHdr{$1, *$1}; v3Global.setHasSCTextSections(); } - | yaSCCTOR { $$ = new AstScCtor{$1, *$1}; v3Global.setHasSCTextSections(); } - | yaSCDTOR { $$ = new AstScDtor{$1, *$1}; v3Global.setHasSCTextSections(); } + | vlScBlock { $$ = $1; } | yVL_HIER_BLOCK { $$ = new AstPragma{$1, VPragmaType::HIER_BLOCK}; } | yVL_INLINE_MODULE { $$ = new AstPragma{$1, VPragmaType::INLINE_MODULE}; } | yVL_NO_INLINE_MODULE { $$ = new AstPragma{$1, VPragmaType::NO_INLINE_MODULE}; } | yVL_PUBLIC_MODULE { $$ = new AstPragma{$1, VPragmaType::PUBLIC_MODULE}; v3Global.dpi(true); } ; +vlScBlock: // Verilator-specific `systemc_* blocks + yaSCHDR { $$ = new AstScHdr{$1, *$1}; v3Global.setHasSCTextSections(); } + | yaSCINT { $$ = new AstScInt{$1, *$1}; v3Global.setHasSCTextSections(); } + | yaSCIMP { $$ = new AstScImp{$1, *$1}; v3Global.setHasSCTextSections(); } + | yaSCIMPH { $$ = new AstScImpHdr{$1, *$1}; v3Global.setHasSCTextSections(); } + | yaSCCTOR { $$ = new AstScCtor{$1, *$1}; v3Global.setHasSCTextSections(); } + | yaSCDTOR { $$ = new AstScDtor{$1, *$1}; v3Global.setHasSCTextSections(); } + ; + + module_or_generate_item: // ==IEEE: module_or_generate_item // // IEEE: parameter_override yDEFPARAM list_of_defparam_assignments ';' { $$ = $2; } @@ -7372,6 +7377,8 @@ class_item: // ==IEEE: class_item // // local_parameter_declaration under parameter_declaration | parameter_declaration ';' { $$ = $1; } | ';' { $$ = nullptr; } + // // Verilator specific + | vlScBlock { $$ = $1; } // | error ';' { $$ = nullptr; } ; diff --git a/test_regress/t/t_extend_class.py b/test_regress/t/t_extend_class.py new file mode 100755 index 000000000..fc5a55e3f --- /dev/null +++ b/test_regress/t/t_extend_class.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('vlt') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_extend_class.v b/test_regress/t/t_extend_class.v new file mode 100644 index 000000000..d5bd34405 --- /dev/null +++ b/test_regress/t/t_extend_class.v @@ -0,0 +1,56 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2003-2007 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +// Although strange, Verilog defines are expanded inside the C blocks +// (as the `systemc_* directives are opaque to the preprocessor) +`define finished "*-* All Finished *-*\n" + +class Cls; +`ifdef verilator + `systemc_header +#define DID_INT_HEADER 1 + `systemc_interface +#ifndef DID_INT_HEADER +#error "`systemc_header didn't work" +#endif + bool m_did_ctor; + uint32_t my_function() { + if (!m_did_ctor) vl_fatal(__FILE__, __LINE__, __FILE__, "`systemc_ctor didn't work"); + return 1; + } + static void my_imp_function(); + + `systemc_imp_header +#define DID_IMP_HEADER 1 + `systemc_implementation + + void `systemc_class_name::my_imp_function() { } + + `systemc_ctor // Works, but using a $c inside a `function new` might be cleaner + m_did_ctor = 1; + `systemc_dtor + printf("In systemc_dtor\n"); + printf(`finished); + `verilog + +`endif // verilator + +endclass + +module t (/*AUTOARG*/); + + int i; + + initial begin + Cls c; + c = new; + i = $c(c, "->my_function()"); + $c(c, "->my_imp_function();"); + c = null; // Causes destruction and All Finished + $finish; + end + +endmodule