verilator/src/V3ParseGrammar.h

338 lines
16 KiB
C
Raw Normal View History

// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Parse syntax tree
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3Ast.h"
#include "V3Control.h"
#include "V3Global.h"
#include "V3ParseImp.h" // Defines YYTYPE; before including bison header
#include <stack>
class V3ParseGrammar final {
public:
AstVar* m_varAttrp = nullptr; // Current variable for attribute adding
AstRange* m_gateRangep = nullptr; // Current range for gate declarations
AstNode* m_scopedSigAttr = nullptr; // Pointer to default signal attribute
AstCase* m_caseAttrp = nullptr; // Current case statement for attribute adding
AstNodeDType* m_varDTypep = nullptr; // Pointer to data type for next signal declaration
AstNodeDType* m_memDTypep = nullptr; // Pointer to data type for next member declaration
2025-09-10 23:42:45 +02:00
AstDelay* m_netDelayp = nullptr; // Pointer to delay for next signal declaration
AstStrengthSpec* m_netStrengthp = nullptr; // Pointer to strength for next net declaration
FileLine* m_instModuleFl = nullptr; // Fileline of module referenced for instantiations
AstPin* m_instParamp = nullptr; // Parameters for instantiations
string m_instModule; // Name of module referenced for instantiations
VVarType m_varDecl = VVarType::UNKNOWN; // Type for next signal declaration (reg/wire/etc)
VDirection m_varIO = VDirection::NONE; // Direction for next signal declaration (reg/wire/etc)
VLifetime m_varLifetime; // Static/Automatic for next signal
bool m_impliedDecl = false; // Allow implied wire declarations
bool m_varDeclTyped = false; // Var got reg/wire for dedup check
bool m_pinAnsi = false; // In ANSI parameter or port list
bool m_tracingParse = true; // Tracing disable for parser
bool m_inImplements = false; // Is inside class implements list
bool m_insideProperty = false; // Is inside property declaration
bool m_specifyignWarned = false; // Issued a SPECIFYIGN warning
bool m_typedPropertyPort = false; // Typed property port occurred on port lists
bool m_modportImpExpActive
= false; // Standalone ID is a tf_identifier instead of port_identifier
bool m_modportImpExpLastIsExport
= false; // Last import_export statement in modportPortsDecl is an export
int m_pinNum = -1; // Pin number currently parsing
std::stack<int> m_pinStack; // Queue of pin numbers being parsed
static int s_typeImpNum; // Implicit type number, incremented each module
// CONSTRUCTORS
V3ParseGrammar() {}
static V3ParseGrammar* singletonp() {
static V3ParseGrammar singleton;
return &singleton;
}
// METHODS
AstArg* argWrapList(AstNodeExpr* nodep) VL_MT_DISABLED;
bool allTracingOn(FileLine* fl) {
return v3Global.opt.trace() && m_tracingParse && fl->tracingOn();
}
AstRange* scrubRange(AstNodeRange* rangep) VL_MT_DISABLED;
AstNodePreSel* scrubSel(AstNodeExpr* fromp, AstNodePreSel* selp) VL_MT_DISABLED;
AstNodeDType* createArray(AstNodeDType* basep, AstNodeRange* rangep,
bool isPacked) VL_MT_DISABLED;
AstVar* createVariable(FileLine* fileline, const string& name, AstNodeRange* arrayp,
AstNode* attrsp) VL_MT_DISABLED;
AstAssignW* createSupplyExpr(FileLine* fileline, const string& name, int value) VL_MT_DISABLED;
Internals: Refactor text based Ast constructs (#6280) (#6571) Remove the large variety of ways raw "text" is represented in the Ast. Particularly, the only thing that represents a string to be emitted in the output is AstText. There are 5 AstNodes that can contain AstText, and V3Emit will throw an error if an AstText is encountered anywhere else: - AstCStmt: Internally generated procedural statements involving raw text. - AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to AstCStmt, as it's largely equivalent. We should never create this internally unless used to represent user input. It is used for $c, statements in the input, and for some 'systemc_* blocks. - AstCExpr: Internally generaged expression involving raw text. - AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to AstCExpr. It is largely equivalent, but also has more optimizations disabled. This should never be created internally, it is only used for $c expressions in the input. - AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical wrappers. Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and AstTextBlock, as these are always generated by us, and should always be well formed. Tracking is always off for AstCStmtUser and AstCExprUser, as these contain arbitrary user input that might not be safe to parse for indentation. Remove subsequently redundant AstNodeSimpleText and AstNodeText types. This patch also fixes incorrect indentation in emitted waveform tracing functions, and makes the output more readable for hier block SV stubs. With that, all raw text nodes are handled as a proper AstNodeStmt or AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
std::string textQuoted(FileLine* fileline, const std::string& text) {
return singletonp()->unquoteString(fileline, text);
}
AstNode* createCell(FileLine* fileline, const string& name, AstPin* pinlistp,
AstNodeRange* rangelistp) {
// Must clone m_instParamp as may be comma'ed list of instances
AstCell* const nodep = new AstCell{
fileline,
singletonp()->m_instModuleFl,
name,
singletonp()->m_instModule,
pinlistp,
(singletonp()->m_instParamp ? singletonp()->m_instParamp->cloneTree(true) : nullptr),
singletonp()->scrubRange(rangelistp)};
nodep->trace(singletonp()->allTracingOn(fileline));
return nodep;
}
void createCoverGroupMethods(AstClass* nodep, AstNode* sampleArgs) {
// Hidden static to take unspecified reference argument results
AstVar* const defaultVarp
= new AstVar{nodep->fileline(), VVarType::MEMBER, "__Vint", nodep->findIntDType()};
defaultVarp->lifetime(VLifetime::STATIC_EXPLICIT);
nodep->addStmtsp(defaultVarp);
// IEEE: function void sample()
AstFunc* const funcp = new AstFunc{nodep->fileline(), "sample", nullptr, nullptr};
funcp->addStmtsp(sampleArgs);
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
// IEEE: function void start(), void stop()
for (const string& name : {"start"s, "stop"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
}
// IEEE: static function real get_coverage(optional ref int, optional ref int)
// IEEE: function real get_inst_coverage(optional ref int, optional ref int)
for (const string& name : {"get_coverage"s, "get_inst_coverage"s}) {
AstFunc* const funcp = new AstFunc{nodep->fileline(), name, nullptr, nullptr};
funcp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
funcp->isStatic(name == "get_coverage");
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
{
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, name,
nodep->findDoubleDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::OUTPUT);
varp->funcReturn(true);
funcp->fvarp(varp);
}
for (const string& varname : {"covered_bins"s, "total_bins"s}) {
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, varname,
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
varp->valuep(new AstVarRef{nodep->fileline(), defaultVarp, VAccess::READ});
funcp->addStmtsp(varp);
}
}
// IEEE: function void set_inst_name(string)
{
AstFunc* const funcp
= new AstFunc{nodep->fileline(), "set_inst_name", nullptr, nullptr};
funcp->classMethod(true);
funcp->dtypep(funcp->findVoidDType());
nodep->addMembersp(funcp);
AstVar* const varp = new AstVar{nodep->fileline(), VVarType::MEMBER, "name",
nodep->findStringDType()};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
varp->funcLocal(true);
varp->direction(VDirection::INPUT);
funcp->addStmtsp(varp);
}
}
AstDisplay* createDisplayError(FileLine* fileline) {
AstDisplay* nodep = new AstDisplay{fileline, VDisplayType::DT_ERROR, "", nullptr, nullptr};
AstNode::addNext<AstNode, AstNode>(nodep, new AstStop{fileline, false});
return nodep;
}
AstNodeExpr* createGatePin(AstNodeExpr* exprp) {
AstRange* const rangep = m_gateRangep;
if (!rangep) {
return exprp;
} else {
return new AstGatePin{rangep->fileline(), exprp, rangep->cloneTree(true)};
}
}
AstSenTree* createSenTreeChanged(FileLine* fl, AstNodeExpr* exprp) {
return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_CHANGED, exprp}};
}
AstSenTree* createSenTreeDotStar(FileLine* fl) {
return new AstSenTree{fl, new AstSenItem{fl, VEdgeType::ET_COMBO_STAR, nullptr}};
}
AstNodeExpr* createGlobalClockParseRef(FileLine* fl) {
return new AstParseRef{fl, "__024global_clock", nullptr, nullptr};
}
AstSenTree* createGlobalClockSenTree(FileLine* fl) {
return createSenTreeChanged(fl, createGlobalClockParseRef(fl));
}
AstNode* createNettype(FileLine* fl, const string& name) {
// As nettypes are unsupported, we just alias to logic
AstTypedef* const nodep = new AstTypedef{fl, name, nullptr, VFlagChildDType{},
new AstBasicDType{fl, VFlagLogicPacked{}, 1}};
V3ParseImp::parsep()->tagNodep(nodep);
return nodep;
}
AstNode* createTypedef(FileLine* fl, const string& name, AstNode* attrsp, AstNodeDType* basep,
AstNodeRange* rangep) {
AstTypedef* const nodep = new AstTypedef{fl, name, attrsp, VFlagChildDType{},
singletonp()->createArray(basep, rangep, false)};
V3ParseImp::parsep()->tagNodep(nodep);
return nodep;
}
AstNode* createTypedefFwd(FileLine* fl, const string& name, const VFwdType& fwdType) {
AstTypedefFwd* const nodep = new AstTypedefFwd{fl, name, fwdType};
V3ParseImp::parsep()->tagNodep(nodep);
return nodep;
}
void endLabel(FileLine* fl, AstNode* nodep, string* endnamep) {
endLabel(fl, nodep->prettyName(), endnamep);
}
void endLabel(FileLine* fl, const string& name, string* endnamep) {
if (fl && endnamep && *endnamep != "" && name != *endnamep
&& name != AstNode::prettyName(*endnamep)) {
fl->v3warn(ENDLABEL, "End label '" << *endnamep << "' does not match begin label '"
<< name << "'");
}
}
void setDType(AstNodeDType* dtypep) {
2025-09-10 23:42:45 +02:00
if (m_varDTypep) {
UASSERT_OBJ(!m_varDTypep->backp(), m_varDTypep, "Should not link directly");
2025-09-09 23:39:44 +02:00
VL_DO_CLEAR(m_varDTypep->deleteTree(), m_varDTypep = nullptr);
}
m_varDTypep = dtypep;
}
2025-09-10 23:42:45 +02:00
void setNetDelay(AstDelay* netDelayp) {
if (m_netDelayp) {
UASSERT_OBJ(!m_netDelayp->backp(), m_netDelayp, "Should not link directly");
VL_DO_CLEAR(m_netDelayp->deleteTree(), m_netDelayp = nullptr);
}
m_netDelayp = netDelayp;
}
void setNetStrength(AstStrengthSpec* netStrengthp) { m_netStrengthp = netStrengthp; }
void pinPush() {
m_pinStack.push(m_pinNum);
m_pinNum = 1;
}
void pinPop(FileLine* fl) {
if (VL_UNCOVERABLE(m_pinStack.empty())) fl->v3fatalSrc("Underflow of pin stack");
m_pinNum = m_pinStack.top();
m_pinStack.pop();
}
AstNodeDType* addRange(AstBasicDType* dtypep, AstNodeRange* rangesp, bool isPacked) {
// If dtypep isn't basic, don't use this, call createArray() instead
if (!rangesp) {
return dtypep;
} else {
// If rangesp is "wire [3:3][2:2][1:1] foo [5:5][4:4]"
// then [1:1] becomes the basicdtype range; everything else is arraying
// the final [5:5][4:4] will be passed in another call to createArray
AstNodeRange* rangearraysp = nullptr;
if (dtypep->isRanged()) {
rangearraysp = rangesp; // Already a range; everything is an array
} else {
AstNodeRange* finalp = rangesp;
while (finalp->nextp()) finalp = VN_CAST(finalp->nextp(), Range);
if (finalp != rangesp) {
finalp->unlinkFrBack();
rangearraysp = rangesp;
}
if (AstRange* const finalRangep = VN_CAST(finalp, Range)) { // not an UnsizedRange
if (dtypep->implicit()) {
// It's no longer implicit but a wire logic type
AstBasicDType* const newp = new AstBasicDType{
dtypep->fileline(), VBasicDTypeKwd::LOGIC, dtypep->numeric(),
dtypep->width(), dtypep->widthMin()};
VL_DO_DANGLING(dtypep->deleteTree(), dtypep);
dtypep = newp;
}
dtypep->rangep(finalRangep);
}
}
return createArray(dtypep, rangearraysp, isPacked);
}
}
string unquoteString(FileLine* fileline, const std::string& text) VL_MT_DISABLED;
void checkDpiVer(FileLine* fileline, const string& str) {
2025-09-20 14:19:42 +02:00
if (str != "DPI-C" && !v3Global.opt.bboxSys())
fileline->v3warn(E_UNSUPPORTED, "Unsupported DPI type '"
<< str
<< "': Use 'DPI-C' (IEEE 1800-2023 35.5.4)");
}
// Given a list of clocking declarations, put them in clocking items
AstClockingItem* makeClockingItemList(FileLine* flp, const VDirection direction,
AstNodeExpr* skewp, AstNode* const clockingDeclps) {
AstClockingItem* itemsp = nullptr;
for (AstNode *nodep = clockingDeclps, *nextp; nodep; nodep = nextp) {
nextp = nodep->nextp();
if (nextp) nextp->unlinkFrBackWithNext();
if (itemsp && skewp) skewp = skewp->cloneTree(false);
AstClockingItem* itemp = new AstClockingItem{flp, direction, skewp, nodep};
itemsp = itemsp ? itemsp->addNext(itemp) : itemp;
}
return itemsp;
}
void setScopedSigAttr(AstNode* attrsp) {
if (m_scopedSigAttr) { // clearing set attribute
2025-09-11 13:01:36 +02:00
UASSERT_OBJ(!m_scopedSigAttr->backp(), m_scopedSigAttr, "Should not link directly");
VL_DO_DANGLING(m_scopedSigAttr->deleteTree(), m_scopedSigAttr);
}
m_scopedSigAttr = attrsp;
}
void createScopedSigAttr(VAttrType vattrT) {
setScopedSigAttr(new AstAttrOf{V3ParseImp::parsep()->lexFileline(), vattrT});
}
AstNode* cloneScopedSigAttr() const {
return m_scopedSigAttr ? m_scopedSigAttr->cloneTree(true) : nullptr;
}
static void addForkStmtsp(AstFork* forkp, AstNode* stmtsp) {
forkp->addStmtsp(stmtsp);
for (AstNode* stmtp = stmtsp; stmtp; stmtp = stmtp->nextp()) {
AstVar* const varp = VN_CAST(stmtp, Var);
if (!varp) break;
varp->unlinkFrBack();
forkp->addInitsp(varp);
}
}
void createGenericIface(AstNode* const nodep, AstNodeRange* const rangep,
AstNode* sigAttrListp, FileLine* const modportFileline = nullptr,
const string& modportstrp = "") {
m_varDecl = VVarType::GPARAM;
m_varIO = VDirection::NONE;
setDType(new AstParseTypeDType{nodep->fileline(), VFwdType::GENERIC_INTERFACE});
m_varDeclTyped = true;
const std::string uniqueName = "__VGIfaceParam" + nodep->name();
AstNode::addNext(nodep,
createVariable(nodep->fileline(), uniqueName, rangep, sigAttrListp));
m_varDecl = VVarType::IFACEREF;
AstIfaceGenericDType* const refdtypep
= new AstIfaceGenericDType{nodep->fileline(), modportFileline, modportstrp};
setDType(refdtypep);
m_varDeclTyped = true;
m_varIO = VDirection::INPUT;
AstNode::addNext(nodep,
createVariable(nodep->fileline(), nodep->name(), rangep, sigAttrListp));
m_varDecl = VVarType::VAR;
}
};