Internals: Refactor `systemc_* section handling (#6280)

This commit is contained in:
Geza Lore 2025-10-14 11:23:23 +01:00
parent cefcf836fd
commit 0c712d7c60
11 changed files with 121 additions and 114 deletions

View File

@ -1604,6 +1604,37 @@ inline std::ostream& operator<<(std::ostream& os, const VStrength& rhs) {
return os << rhs.ascii();
}
// ######################################################################
// VSystemCSectionType - Represents the type of a `systemc_* block (Verilator specific extension)
class VSystemCSectionType final {
public:
enum en : uint8_t {
CTOR, // `systemc_ctor
DTOR, // `systemc_dtor
HDR, // `systemc_header
HDR_POST, // `systemc_header_post
IMP, // `systemc_implementation
IMP_HDR, // `systemc_imp_header
INT // `systemc_interface
};
enum en m_e;
const char* ascii() const {
static const char* const names[] = {"`systemc_ctor", //
"`systemc_dtor", //
"`systemc_header", //
"`systemc_header_post", //
"`systemc_implementation", //
"`systemc_imp_header", //
"`systemc_interface"};
return names[m_e];
}
// cppcheck-suppress noExplicitConstructor
constexpr VSystemCSectionType(en _e)
: m_e{_e} {}
constexpr operator en() const { return m_e; }
};
//######################################################################
class VTracePrefixType final {

View File

@ -1643,6 +1643,29 @@ public:
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstSystemCSection final : public AstNode {
// Verilator specific "`systemc_* block". This is a "module item"
// containing arbitrary text that is emitted to the C++ output in various
// locations depending on the sectionType.
const VSystemCSectionType m_sectionType; // The section type
const std::string m_text; // The text content
public:
AstSystemCSection(FileLine* fl, VSystemCSectionType sectionType, const std::string& text)
: ASTGEN_SUPER_SystemCSection(fl)
, m_sectionType{sectionType}
, m_text{text} {
v3Global.setHasSystemCSections();
}
ASTGEN_MEMBERS_AstSystemCSection;
VSystemCSectionType sectionType() const { return m_sectionType; }
const std::string& text() const { return m_text; }
void dump(std::ostream&) const override;
void dumpJson(std::ostream&) const override;
bool sameNode(const AstNode*) const override { return false; }
bool isPure() override { return false; }
bool isOutputter() override { return true; }
};
class AstTopScope final : public AstNode {
// A singleton, held under the top level AstModule. Holds the top level
// AstScope, and after V3ActiveTop, the global list of AstSenTrees (list of
@ -2808,64 +2831,6 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
// === AstNodeText ===
class AstScCtor final : public AstNodeText {
public:
AstScCtor(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScCtor(fl, textp) {}
ASTGEN_MEMBERS_AstScCtor;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScDtor final : public AstNodeText {
public:
AstScDtor(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScDtor(fl, textp) {}
ASTGEN_MEMBERS_AstScDtor;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScHdr final : public AstNodeText {
public:
AstScHdr(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScHdr(fl, textp) {}
ASTGEN_MEMBERS_AstScHdr;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScHdrPost final : public AstNodeText {
public:
AstScHdrPost(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScHdrPost(fl, textp) {}
ASTGEN_MEMBERS_AstScHdrPost;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScImp final : public AstNodeText {
public:
AstScImp(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScImp(fl, textp) {}
ASTGEN_MEMBERS_AstScImp;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScImpHdr final : public AstNodeText {
public:
AstScImpHdr(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScImpHdr(fl, textp) {}
ASTGEN_MEMBERS_AstScImpHdr;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
class AstScInt final : public AstNodeText {
public:
AstScInt(FileLine* fl, const string& textp)
: ASTGEN_SUPER_ScInt(fl, textp) {}
ASTGEN_MEMBERS_AstScInt;
bool isPure() override { return false; } // SPECIAL: User may order w/other sigs
bool isOutputter() override { return true; }
};
// === AstNodeSimpleText ===
class AstText final : public AstNodeSimpleText {
public:

View File

@ -2532,6 +2532,14 @@ void AstSliceSel::dumpJson(std::ostream& str) const {
if (declRange().ranged()) dumpJsonStr(str, "declRange", cvtToStr(declRange()));
dumpJsonGen(str);
}
void AstSystemCSection::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " sectionType=" << sectionType().ascii();
}
void AstSystemCSection::dumpJson(std::ostream& str) const {
dumpJsonStr(str, "sectionType", sectionType().ascii());
dumpJsonGen(str);
}
void AstMTaskBody::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " ";

View File

@ -138,8 +138,8 @@ class CCtorsVisitor final : public VNVisitor {
V3CCtorsBuilder* m_varResetp = nullptr; // Builder of _ctor_var_reset
// METHODS
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VNType type) {
auto textAndFileline = EmitCBaseVisitorConst::textSection(modp, type);
static void insertSc(AstCFunc* cfuncp, const AstNodeModule* modp, VSystemCSectionType type) {
auto textAndFileline = EmitCBaseVisitorConst::scSection(modp, type);
if (!textAndFileline.first.empty()) {
AstTextBlock* const newp
= new AstTextBlock{textAndFileline.second, textAndFileline.first, false, false};
@ -177,7 +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::ScDtor);
insertSc(funcp, classp, VSystemCSectionType::DTOR);
classp->addStmtsp(funcp);
}
}
@ -188,7 +188,7 @@ class CCtorsVisitor final : public VNVisitor {
m_varResetp = nullptr;
m_cfuncp = nodep;
iterateChildren(nodep);
if (nodep->name() == "new") insertSc(nodep, m_modp, VNType::ScCtor);
if (nodep->name() == "new") insertSc(nodep, m_modp, VSystemCSectionType::CTOR);
}
void visit(AstVar* nodep) override {
if (nodep->needsCReset()) {

View File

@ -281,26 +281,28 @@ 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);
std::pair<string, FileLine*> EmitCBaseVisitorConst::scSection(const AstNodeModule* modp,
VSystemCSectionType type) {
if (!v3Global.hasSystemCSections()) 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";
AstSystemCSection* const ssp = VN_CAST(nodep, SystemCSection);
if (!ssp) continue;
if (ssp->sectionType() != type) continue;
if (text.empty()) {
fl = ssp->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 (last_line + 1 != nodep->fileline()->lineno() && v3Global.opt.decoration()) {
text += "// From `systemc at " + nodep->fileline()->ascii() + "\n";
}
last_line = ssp->fileline()->lineno();
text += ssp->text();
}
if (!text.empty()) {
if (v3Global.opt.decoration()) text += "//*** Above code from `systemc in Verilog file\n";
@ -315,9 +317,10 @@ std::pair<string, FileLine*> EmitCBaseVisitorConst::textSection(const AstNodeMod
return std::make_pair(text, fl);
}
void EmitCBaseVisitorConst::emitTextSection(const AstNodeModule* modp, VNType type) {
void EmitCBaseVisitorConst::emitSystemCSection(const AstNodeModule* modp,
VSystemCSectionType 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.
auto textAndFileline = textSection(modp, type);
auto textAndFileline = scSection(modp, type);
if (!textAndFileline.first.empty()) ofp()->putsNoTracking(textAndFileline.first);
}

View File

@ -158,8 +158,9 @@ 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);
void emitSystemCSection(const AstNodeModule* modp, VSystemCSectionType type);
static std::pair<string, FileLine*> scSection(const AstNodeModule* modp,
VSystemCSectionType type);
// CONSTRUCTORS
EmitCBaseVisitorConst() = default;

View File

@ -564,7 +564,7 @@ class EmitCHeader final : public EmitCConstInit {
puts("\nclass " + EmitCUtil::symClassName() + ";\n");
// From `systemc_header
emitTextSection(modp, VNType::ScHdr);
emitSystemCSection(modp, VSystemCSectionType::HDR);
emitStructs(modp);
@ -607,7 +607,7 @@ class EmitCHeader final : public EmitCConstInit {
emitFuncDecls(modp, /* inClassBody: */ true);
// From `systemc_interface
emitTextSection(modp, VNType::ScInt);
emitSystemCSection(modp, VSystemCSectionType::INT);
// Close class body
puts("};\n");
@ -616,7 +616,7 @@ class EmitCHeader final : public EmitCConstInit {
// Emit out of class function declarations
puts("\n");
emitFuncDecls(modp, /* inClassBody: */ false);
emitTextSection(modp, VNType::ScHdrPost);
emitSystemCSection(modp, VSystemCSectionType::HDR_POST);
}
explicit EmitCHeader(const AstNodeModule* modp) {

View File

@ -200,7 +200,7 @@ class EmitCImp final : EmitCFunc {
puts("#include \"" + EmitCUtil::pchClassName() + ".h\"\n");
for (const string& name : headers) puts("#include \"" + name + ".h\"\n");
emitTextSection(m_modp, VNType::ScImpHdr);
emitSystemCSection(m_modp, VSystemCSectionType::IMP_HDR);
}
void emitStaticVarDefns(const AstNodeModule* modp) {
@ -286,7 +286,7 @@ class EmitCImp final : EmitCFunc {
putsDecoration(modp, "// Reset structure values\n");
puts(modName + "__" + protect("_ctor_var_reset") + "(this);\n");
emitTextSection(modp, VNType::ScCtor);
emitSystemCSection(modp, VSystemCSectionType::CTOR);
puts("}\n");
}
@ -393,7 +393,7 @@ class EmitCImp final : EmitCFunc {
puts("\n");
putns(modp, EmitCUtil::prefixNameProtect(modp) + "::~" + EmitCUtil::prefixNameProtect(modp)
+ "() {\n");
emitTextSection(modp, VNType::ScDtor);
emitSystemCSection(modp, VSystemCSectionType::DTOR);
puts("}\n");
splitSizeInc(10);
}
@ -487,9 +487,13 @@ class EmitCImp final : EmitCFunc {
if (!modp) return false;
// We always need the slow file
if (m_slow) return true;
// The fast file is only required when we have ScImp nodes
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (VN_IS(nodep, ScImp)) return true;
// The fast file is only required when we have `systemc_implementation nodes
if (v3Global.hasSystemCSections()) {
for (const AstNode* nodep = modp->stmtsp(); nodep; nodep = nodep->nextp()) {
if (const AstSystemCSection* const ssp = VN_CAST(nodep, SystemCSection)) {
if (ssp->sectionType() == VSystemCSectionType::IMP) return true;
}
}
}
return false;
}
@ -507,7 +511,7 @@ class EmitCImp final : EmitCFunc {
emitSavableImp(modp);
} else {
// From `systemc_implementation
emitTextSection(modp, VNType::ScImp);
emitSystemCSection(modp, VSystemCSectionType::IMP);
}
}
void emitCommonImp(const AstNodeModule* modp) {

View File

@ -123,7 +123,7 @@ class V3Global final {
bool m_usesStdPackage = false; // Design uses the std package
bool m_usesTiming = false; // Design uses timing constructs
bool m_hasForceableSignals = false; // Need to apply V3Force pass
bool m_hasSCTextSections = false; // Has `systemc_* sections that need to be emitted
bool m_hasSystemCSections = false; // Has AstSystemCSection that need to be emitted
bool m_useParallelBuild = false; // Use parallel build for model
bool m_useRandomizeMethods = false; // Need to define randomize() class methods
uint64_t m_currentHierBlockCost = 0; // Total cost of this hier block, used for scheduling
@ -197,8 +197,8 @@ public:
void setUsesTiming() { m_usesTiming = true; }
bool hasForceableSignals() const { return m_hasForceableSignals; }
void setHasForceableSignals() { m_hasForceableSignals = true; }
bool hasSCTextSections() const VL_MT_SAFE { return m_hasSCTextSections; }
void setHasSCTextSections() { m_hasSCTextSections = true; }
bool hasSystemCSections() const VL_MT_SAFE { return m_hasSystemCSections; }
void setHasSystemCSections() { m_hasSystemCSections = true; }
V3HierGraph* hierGraphp() const { return m_hierGraphp; }
void hierGraphp(V3HierGraph* graphp) { m_hierGraphp = graphp; }
bool useParallelBuild() const { return m_useParallelBuild; }

View File

@ -469,20 +469,15 @@ class LinkResolveVisitor final : public VNVisitor {
}
}
void visit(AstScCtor* nodep) override {
// Constructor info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
}
void visit(AstScDtor* nodep) override {
// Destructor info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
}
void visit(AstScInt* nodep) override {
// Special class info means the module must remain public
m_modp->modPublic(true);
iterateChildren(nodep);
void visit(AstSystemCSection* nodep) override {
switch (nodep->sectionType()) {
// Constructor, desctructor or special class info means the module must remain public
case VSystemCSectionType::CTOR:
case VSystemCSectionType::DTOR:
case VSystemCSectionType::INT: m_modp->modPublic(true); break;
default: break;
}
// Has no children
}
void visit(AstIfaceRefDType* nodep) override {

View File

@ -2575,13 +2575,13 @@ non_port_module_item<nodep>: // ==IEEE: non_port_module_item
;
vlScBlock<nodep>: // Verilator-specific `systemc_* blocks
yaSCHDR { $$ = new AstScHdr{$<fl>1, *$1}; v3Global.setHasSCTextSections(); }
| yaSCHDRP { $$ = new AstScHdrPost{$<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(); }
yaSCHDR { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::HDR, *$1}; }
| yaSCHDRP { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::HDR_POST, *$1}; }
| yaSCINT { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::INT, *$1}; }
| yaSCIMP { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::IMP, *$1}; }
| yaSCIMPH { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::IMP_HDR, *$1}; }
| yaSCCTOR { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::CTOR, *$1}; }
| yaSCDTOR { $$ = new AstSystemCSection{$<fl>1, VSystemCSectionType::DTOR, *$1}; }
;