Support `systemc_interface and related inside `class`.

This commit is contained in:
Wilson Snyder 2025-03-28 22:04:29 -04:00
parent 6edf2f80a2
commit b4ef6ce860
13 changed files with 175 additions and 52 deletions

View File

@ -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.]

View File

@ -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

View File

@ -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()) {

View File

@ -285,27 +285,43 @@ void EmitCBaseVisitorConst::emitModCUse(const AstNodeModule* modp, VUseType useT
if (nl) puts("\n");
}
std::pair<string, FileLine*> 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);
}

View File

@ -164,6 +164,7 @@ public:
}
void emitModCUse(const AstNodeModule* modp, VUseType useType);
void emitTextSection(const AstNodeModule* modp, VNType type);
static std::pair<string, FileLine*> textSection(const AstNodeModule* modp, VNType type);
// CONSTRUCTORS
EmitCBaseVisitorConst() = default;

View File

@ -162,7 +162,7 @@ class LinkCellsVisitor final : public VNVisitor {
return modp;
}
// VISITs
// VISITORS
void visit(AstNetlist* nodep) override {
readModNames();
iterateChildren(nodep);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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);

View File

@ -2729,18 +2729,23 @@ non_port_module_item<nodep>: // ==IEEE: non_port_module_item
{ $$ = nullptr; BBUNSUP(CRELINE(), "Unsupported: interface decls within module decls"); }
| timeunits_declaration { $$ = $1; }
// // Verilator specific
| yaSCHDR { $$ = new AstScHdr{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCINT { $$ = new AstScInt{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCIMP { $$ = new AstScImp{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCIMPH { $$ = new AstScImpHdr{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCCTOR { $$ = new AstScCtor{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCDTOR { $$ = new AstScDtor{$<fl>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<nodep>: // Verilator-specific `systemc_* blocks
yaSCHDR { $$ = new AstScHdr{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCINT { $$ = new AstScInt{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCIMP { $$ = new AstScImp{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCIMPH { $$ = new AstScImpHdr{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCCTOR { $$ = new AstScCtor{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCDTOR { $$ = new AstScDtor{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
;
module_or_generate_item<nodep>: // ==IEEE: module_or_generate_item
// // IEEE: parameter_override
yDEFPARAM list_of_defparam_assignments ';' { $$ = $2; }
@ -7372,6 +7377,8 @@ class_item<nodep>: // ==IEEE: class_item
// // local_parameter_declaration under parameter_declaration
| parameter_declaration ';' { $$ = $1; }
| ';' { $$ = nullptr; }
// // Verilator specific
| vlScBlock { $$ = $1; }
//
| error ';' { $$ = nullptr; }
;

View File

@ -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()

View File

@ -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