From a7bea996bff854caec2ae3df72c5837681eed5f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 1 Oct 2025 13:00:10 +0200 Subject: [PATCH 01/20] feat: Add V3Instrumentation-Pass --- src/CMakeLists.txt | 2 + src/Makefile_obj.in | 1 + src/V3Control.cpp | 44 ++ src/V3Control.h | 28 + src/V3Instrumentation.cpp | 1098 +++++++++++++++++++++++++++++++++++++ src/V3Instrumentation.h | 33 ++ src/V3Options.cpp | 2 + src/V3Options.h | 2 + src/Verilator.cpp | 9 + src/verilog.l | 4 + src/verilog.y | 6 + 11 files changed, 1229 insertions(+) create mode 100644 src/V3Instrumentation.cpp create mode 100644 src/V3Instrumentation.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 08e70f01c..2c3b9f3e2 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -116,6 +116,7 @@ set(HEADERS V3Inline.h V3Inst.h V3InstrCount.h + V3Instrumentation.h V3Interface.h V3LangCode.h V3LanguageWords.h @@ -287,6 +288,7 @@ set(COMMON_SOURCES V3Inline.cpp V3Inst.cpp V3InstrCount.cpp + V3Instrumentation.cpp V3Interface.cpp V3Life.cpp V3LifePost.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 983f26e62..16725d2fd 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -286,6 +286,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Inline.o \ V3Inst.o \ V3InstrCount.o \ + V3Instrumentation.o \ V3Interface.o \ V3Life.o \ V3LifePost.o \ diff --git a/src/V3Control.cpp b/src/V3Control.cpp index fb0f88d09..9e354abb7 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -576,6 +576,7 @@ class V3ControlResolver final { uint8_t m_mode = NONE; std::unordered_map m_hierWorkers; FileLine* m_profileFileLine = nullptr; + std::map m_instrCfg; V3ControlResolver() = default; ~V3ControlResolver() = default; @@ -640,6 +641,41 @@ public: return cost; } } + // Helper for adding targets to the instrumentation config map + std::pair splitPrefixAndVar(const string& target) { + auto pos = target.rfind('.'); + if (pos == string::npos) { + // No prefix, return error + } + string prefix = target.substr(0, pos); + string varTarget = target.substr(pos + 1); + return {prefix, varTarget}; + } + // Add the instrumentation config data to the map to create the initial map (Used in verilog.y) + void addInstrumentationConfigs(FileLine* fl, const string& instrFunction, int instrID, + const string& target) { + // Error MSG if the instrumentation of the top module is not possible + if ((std::count(target.begin(), target.end(), '.') < 2)) { + v3fatal("In .vlt defined target tries to instrument the highest MODULE, is not possible!" + " ... Target string: " << target); + } + // Implement custom iterator to remove the last part of the target and insert it into the vector of the map + // If the target string is the same as one already in the map, push the var to the vector + auto [prefix, varTarget] = splitPrefixAndVar(target); + InstrumentationEntry entry{instrID, instrFunction, varTarget}; + auto it = m_instrCfg.find(prefix); + if (it != m_instrCfg.end()) { + it->second.entries.push_back(entry); + } else { + // Create a new entry in the map + InstrumentationTarget newTarget; + newTarget.entries.push_back(entry); + m_instrCfg[prefix] = std::move(newTarget); + } + } + std::map& getInstrumentationConfigs() { + return m_instrCfg; + } }; //###################################################################### @@ -698,6 +734,11 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) { V3ControlResolver::s().modules().at(module).addModulePragma(pragma); } +void V3Control::addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, + int instrID, const string& target) { + V3ControlResolver::s().addInstrumentationConfigs(fl, instrumentationfunc, instrID, target); +} + void V3Control::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { V3ControlResolver::s().addProfileData(fl, hierDpi, cost); } @@ -859,6 +900,9 @@ int V3Control::getHierWorkers(const string& model) { FileLine* V3Control::getHierWorkersFileLine(const string& model) { return V3ControlResolver::s().getHierWorkersFileLine(model); } +std::map& V3Control::getInstrumentationConfigs() { + return V3ControlResolver::s().getInstrumentationConfigs(); +} uint64_t V3Control::getProfileData(const string& hierDpi) { return V3ControlResolver::s().getProfileData(hierDpi); } diff --git a/src/V3Control.h b/src/V3Control.h index 8a7a9070f..e95978536 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -26,6 +26,31 @@ #include "V3Mutex.h" //###################################################################### +struct LengthThenLexiographic final { + // Used to sort strings by length, then lexicographically + bool operator()(const string& a, const string& b) const { + if (a.length() != b.length()) return a.length() < b.length(); + return a < b; + } +}; +struct InstrumentationEntry final { + int instrID; + std::string instrFunc; + std::string varTarget; + AstVar* origVarps; + AstVar* instrVarps; +}; +struct InstrumentationTarget final { + std::vector entries; + AstModule* origModulep; + AstModule* instrModulep; + AstModule* topModulep; + AstModule* pointingModulep; + AstCell* cellp; + bool processed = false; + bool done = false; + bool multipleCellps = false; +}; class V3Control final { public: @@ -44,6 +69,9 @@ public: static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents, const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); + static void addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, + int instrID, const string& target); + static std::map& getInstrumentationConfigs(); static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, diff --git a/src/V3Instrumentation.cpp b/src/V3Instrumentation.cpp new file mode 100644 index 000000000..8d0dc523a --- /dev/null +++ b/src/V3Instrumentation.cpp @@ -0,0 +1,1098 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: +// +// 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 +// +//************************************************************************* +// V3Instrumentation's Transformations: +// The instrumentation configuration map is populated with the relevant nodes, as defined by the +// target string specified in the instrumentation configuration within the .vlt file. +// Additionally, the AST (Abstract Syntax Tree) is modified to insert the necessary extra nodes +// required for instrumentation. +// Furthermore, the links between Module, Cell, and Var nodes are adjusted to ensure correct +// connectivity for instrumentation purposes. +//************************************************************************* + +#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3Instrumentation.h" + +#include "V3Control.h" +#include "V3File.h" + +#include +#include +#include +#include +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; + +//################################################################################## +// Instrumentation class finder +class InstrumentationTargetFinder final : public VNVisitor { + AstNetlist* m_netlist = nullptr; // Enable traversing from the beginning if the visitor is to deep + AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node + AstModule* m_modp = nullptr; // Stores the current modulep the visitor is looking at + AstModule* m_targetModp = nullptr; // Stores the targeted modulep + bool m_error = false; // Displays if there was already an error message earlier + bool m_foundCellp = false; // If the visitor found the relevant instance + bool m_foundModp = false; // If the visitor found the relevant model + bool m_foundVarp = false; // If the visitor found the relevant variable + bool m_initModp = true; // If the visitor is in the first module node of the netlist + size_t m_instrIdx = 0; + string m_currHier; // Stores the current hierarchy of the visited nodes (Module, Cell, Var) + string m_target; // Stores the currently visited target string from the config map + + // METHODS + //---------------------------------------------------------------------------------- + AstModule* findModp(AstNetlist* netlist, AstModule* modp) { + for (AstNode* n = netlist->op1p(); n; n = n->nextp()) { + if (VN_IS(n, Module) && VN_CAST(n, Module) == modp) { return VN_CAST(n, Module); } + } + return nullptr; + } + // Helper function to compare the target string starts with the given prefix + bool cmpPrefix(const string& prefix, const string& target ) { + if (target.compare(0, prefix.size(), prefix) == 0 + && (target.size() == prefix.size() || target[prefix.size()] == '.')) { + return true; + } + return false; + } + // Helper function to check if a parameter was already added to the tree previously + bool hasParam(AstModule* modp) { + for (AstNode* n = modp->op2p(); n; n = n->nextp()) { + if (n->name() == "INSTRUMENT") { + return true; + } + } + return false; + } + // Helper function to check if a pin was already added to the tree previously + bool hasPin(AstCell* cellp) { + for (AstNode* n = cellp->paramsp(); n; n = n->nextp()) { + if (n->name() == "INSTRUMENT") { + return true; + } + } + return false; + } + // Check if the multipleCellps flag is set for the given target + bool hasMultiple(const std::string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + return it->second.multipleCellps; + } + return false; + } + // Check if the direct predecessor in the target string has been instrumented, + // to create the correct link between the already instrumented module and the current one. + bool hasPrior(AstModule* modulep, const string& target) { + const auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto priorTarget = reduce2Depth(split(target), KeyDepth::RelevantModule); + auto it = instrCfg.find(priorTarget); + return it != instrCfg.end() + && it->second.processed; + } + bool targetHasFullName(const string& fullname, const string& target) { + return fullname == target; + } + // Check if the given current Hierarchy matches the top module of the target (Pos: 0) + bool targetHasTop(const string& currHier, const string& target) { + return currHier == reduce2Depth(split(target), KeyDepth::TopModule); + } + // Check if the current hierarhy string matches the target string until the depth + // to the module that includes the cell/instance pointing to the targeted module + bool targetHasPointingMod(const string& pointingModuleName, const string& target) { + return pointingModuleName == reduce2Depth(split(target), KeyDepth::RelevantModule); + } + // Check if the given prefix matches the beginning of the current target string + bool targetHasPrefix(const string& prefix, const string& target) { + return cmpPrefix(prefix, target); + } + // Helper Function to split a string by '.' and return a vector of tokens + std::vector split(const std::string& str) { + static const std::regex dot_regex("\\."); + std::sregex_token_iterator iter(str.begin(), str.end(), dot_regex, -1); + std::sregex_token_iterator end; + return std::vector(iter, end); + } + // Helper function to reduce a given key to a certain hierarchy level. + enum class KeyDepth { + TopModule = 0, + RelevantModule = 1, + Instance = 2, + FullKey = 3 + }; + string reduce2Depth(std::vector keyTokens, KeyDepth hierarchyLevel) { + std::string reducedKey = keyTokens[0]; + if (hierarchyLevel == KeyDepth::TopModule) { + return keyTokens[0]; + } else { + int d = static_cast(hierarchyLevel); + for (size_t i = 1; i < keyTokens.size() - d; ++i) { + reducedKey += "." + keyTokens[i]; + } + return reducedKey; + } + } + // Helper function for adding the parameters into the tree + void addParam(AstModule* modp) { + AstVar* paramp = new AstVar(modp->fileline(), VVarType::GPARAM, "INSTRUMENT", + VFlagChildDType{}, nullptr); + paramp->valuep( + new AstConst(modp->fileline(), AstConst::Signed32{}, 0)); + paramp->dtypep(paramp->valuep()->dtypep()); + paramp->ansi(true); + modp->addStmtsp(paramp); + } + // Helper function for adding the parameters into the tree + void addPin(AstCell* cellp, bool isInstrumentPath) { + int pinnum = 0; + if (isInstrumentPath) { + for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } + AstPin* pinp + = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", + // The pin is set to 1 to enable the instrumentation path + new AstConst(cellp->fileline(), AstConst::Signed32{}, 1)); + pinp->param(true); + cellp->addParamsp(pinp); + } else { + for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } + AstPin* pinp + = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", + new AstParseRef(cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT")); + pinp->param(true); + cellp->addParamsp(pinp); + } + } + // Edit the instrumentation data for the cell in the map + void editInstrData(AstCell* cellp, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.cellp = cellp; + } + } + // Edit the instrumentation data for the pointing module in the map + void editInstrData(AstModule* modulep, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.pointingModulep = modulep; + } + } + // Check for multiple cells pointing to the next module + void multCellForModp(AstCell* cellp) { + std::multiset cellModps; + for (AstNode* n = m_modp->op2p(); n; n = n->nextp()) { + if (VN_IS(n, Cell)) { cellModps.insert(VN_CAST(n, Cell)->modp()); } + } + m_modp = nullptr; + m_cellModp = cellp->modp(); + auto modpRepetition = cellModps.count(m_cellModp); + if (modpRepetition > 1 + && !targetHasFullName(m_currHier, m_target)) { + setMultiple(m_target); + } + } + // Insert the cell node that is/will pointing/point to the targeted module + void setCell(AstCell* cellp, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.cellp = cellp; + } + } + // Insert the original and instrumented module nodes to the map + void setInstrModule(AstModule* origModulep, AstModule* instrModulep, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.origModulep = origModulep; + it->second.instrModulep = instrModulep; + } + } + // Set the multipleCellps flag + void setMultiple(const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.multipleCellps = true; + } + } + // Insert the module node that includes the cell pointing to the targeted module + // to the map + void setPointingMod(AstModule* modulep, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.pointingModulep = modulep; + } + } + // Set the processed flag + void setProcessed(const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.processed = true; + } + } + // Insert the top module node of the netlist to the map + void setTopMod(AstModule* modulep, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + it->second.topModulep = modulep; + } + } + // Insert the original and instrumented variable nodes to the map + void setVar(AstVar* varp, AstVar* instVarp, const string& target) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto it = instrCfg.find(target); + if (it != instrCfg.end()) { + for (auto& entry : it->second.entries) { + if (entry.varTarget == varp->name()) { + entry.origVarps = varp; + entry.instrVarps = instVarp; + return; + } + } + } + } + + // VISITORS + //---------------------------------------------------------------------------------- + + //ASTMODULE VISITOR FUNCTION: + //Iterates over the existing module nodes in the netlist. + //For the first module in the netlist the node name is checked if it is at the first position in + //the target string provided by the configuration file. If not an error is thown, otherwise the + //modules is checked for an already existing INSTRUMENT parameter. If there is no INSTRUMENT + //parameter present we add it to the module. This parameter is used to control the + //instrumentation of the target. The module is then added to the map of the instrumentation + //configs as the top module. Additionally the hierarchy the function viewed is currently add is + //initialized with the module name. This module hierarchy is used to identify the correct target + //path in the netlist. The function iterates over the children of the module, with the Cells and + //Vars beeing the relevant targets. + + //After the iteration of the children the m_modp variable needs to be set by the Cell visitor to + //continue or there needs no suitable cell to be found. (See CELL VISITOR FUNCTION & VAR VISITOR + //FUNCTION) Since the module from the m_modp can appear earlier in the tree the fundModp function + //is used to iterate over the netlift from the beginning to find the module. The module node + //displayed by the m_modp variable is then checked if this is the module containing the target + //variable (relevant module) or if it the module containing the cell pointing to the relevant + //module (pointing module). If the module node suits one of these two conditions the module nodes + //are added to the instrumentation configs map. Independetly from these conditions the INSTRUMENT + //parameter is added to the module nodes in the target path. This parameter is used to control + //the instrumentation of the target. + void visit (AstModule* nodep) { + if (m_initModp) { + if (targetHasTop(nodep->name(), m_target)) { + m_foundModp = true; + m_modp = nodep; + m_currHier = nodep->name(); + if (!hasParam(nodep)) { + addParam(nodep); + } + if (string::npos == m_target.rfind('.')) { + m_targetModp = nodep; + m_foundCellp = true; // Set to true since there is no Instance that the cell visitor could find + } + setTopMod(nodep, m_target); + iterateChildren(nodep); + } else if (!m_foundModp && nodep->name() == "@CONST-POOL@") { + v3error("Verilator-configfile': could not find initial 'module' in 'module.instance.__'" + " ... Target: '" + << m_target << "'"); + m_initModp = false; + m_error = true; + } + } else if (m_cellModp != nullptr && (nodep = findModp(m_netlist, VN_CAST(m_cellModp, Module))) != nullptr) { + if (targetHasFullName(m_currHier, m_target)) { + AstModule* instrModp = nullptr; + m_foundModp = true; + m_targetModp = nodep; + m_cellModp = nullptr; + // Check for prior changes made to the tree + if (hasPrior(nodep, m_currHier)) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + instrModp = instrCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule))->second.instrModulep; + editInstrData(instrModp, m_currHier); + AstCell* cellp = nullptr; + for (AstNode* n = instrModp->op2p(); n; n = n->nextp()) { + if (VN_IS(n, Cell) && (VN_CAST(n, Cell)->modp() == nodep) && instrCfg.find(m_currHier)->second.cellp->name() == n->name()) { + cellp = VN_CAST(n, Cell); + break; + } + } + editInstrData(cellp, m_currHier); + } + if (!hasParam(nodep)) { + addParam(nodep); + } + instrModp = nodep->cloneTree(false); + instrModp->name(nodep->name() + "__inst__" + std::to_string(m_instrIdx)); + if (hasMultiple(m_target)) { instrModp->inLibrary(true); } + setInstrModule(nodep, instrModp, m_target); + iterateChildren(nodep); + } else if (targetHasPointingMod(m_currHier, m_target)) { + m_foundModp = true; + m_foundCellp = false; + m_modp = nodep; + m_cellModp = nullptr; + if (!hasParam(nodep)) { + addParam(nodep); + } + setPointingMod(nodep, m_target); + iterateChildren(nodep); + } else if (targetHasPrefix(m_currHier, m_target)) { + m_foundModp = true; + m_foundCellp = false; + m_modp = nodep; + m_cellModp = nullptr; + if (!hasParam(nodep)) { + addParam(nodep); + } + iterateChildren(nodep); + } + } else if(!m_error && !m_foundCellp) { + v3error("Verilator-configfile: could not find 'instance' in " + "'__.instance.__' ... Target string: '" + << m_target << "'"); + } else if(!m_error && !m_foundVarp) { + v3error("Verilator-configfile': could not find '.var' in '__.module.var'" + " ... Target: '" + << m_target << "'"); + } + } + + //ASTCELL VISITOR FUNCTION: + //This cell visitor function is called if the module visitor function found a module that matches + //the target string from the config. The first function call should be when visiting the initial + //module in the netlist. When a cell is found that matches the target string and is not marked as + //found, the current hierarchy is updated and the cell marked as found. Additionally, if this is + //the cell in the initial module, the initial module flag is set to false. The in the current + //module existing cells are checked if there are multiple cells linking to the next module in the + //target string. After that the m_modp is updated to match the cell's module pointer, which is + //needed for the next call of the module visitor. Next the pin for the INSTRUMENT parameter is + //added to the cell. This parameter is added either as a constant or as a reference, depending on + //the traversal stage. If there are multiple cells linking to the next module in the target + //string, the multiple flag is set in the instrumentation config map. For the inistial module the + //found cell is then added to the instrumentation configuration map with the current hierarchy as + //the target path. Otherwise the cell is added to the instrumentation configuration map, when the + //current hierarchy with the cell name fully matches a target path, with the last two entrances + //removed (Module, Var). This function ensures that the correct cells in the design hierarchy are + //instrumented and tracked, supporting both unique and repeated module instances. + void visit (AstCell* nodep) { + if (m_initModp) { + if (targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { + m_foundCellp = true; + m_foundModp = false; + m_initModp = false; + m_currHier = m_currHier + "." + nodep->name(); + if (!hasPin(nodep)) { + addPin(nodep, false); + } + multCellForModp(nodep); + setCell(nodep, m_target); + } else if (targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { + m_foundCellp = true; + m_foundModp = false; + m_initModp = false; + m_currHier = m_currHier + "." + nodep->name(); + if (!hasPin(nodep)) { + addPin(nodep, true); + } + multCellForModp(nodep); + setCell(nodep, m_target); + } else if (!m_foundCellp && !VN_IS(nodep->nextp(), Cell)) { + v3error("Verilator-configfile': could not find initial 'instance' in " + "'topModule.instance.__' ... Target string: '" + << m_target << "'"); + m_error = true; + m_initModp = false; + } + } else if (m_modp != nullptr && targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { + m_foundCellp = true; + m_foundModp = false; + m_currHier = m_currHier + "." + nodep->name(); + if (!hasPin(nodep)) { + addPin(nodep, false); + } + multCellForModp(nodep); + setCell(nodep, m_target); + } else if (m_modp != nullptr && targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { + m_foundCellp = true; + m_foundModp = false; + m_currHier = m_currHier + "." + nodep->name(); + if (!hasPin(nodep)) { + addPin(nodep, false); + } + multCellForModp(nodep); + } + } + + //ASTVAR VISITOR FUNCTION: + //The var visitor function is used to find the variable that matches the target string from the + //config. This is only done if the Cell visitor does not find a matching cell in the current + //module of the target hierarchy. Since we therefore know that we will not traverse any further + //in the hierarchy of the model, we can check for this variable. If a variable is found, with its + //name added to the current hierarchy, that siuts the target string, an edited version and the + //original version are added to the instrumentation config map. + void visit (AstVar* nodep) { + if (m_targetModp != nullptr) { + const InstrumentationTarget& target = V3Control::getInstrumentationConfigs().find( + m_currHier)->second; + for (const auto& entry : target.entries) { + if (nodep->name() == entry.varTarget) { + int width; + AstBasicDType* basicp = nodep->basicp(); + bool literal = basicp->isLiteralType(); + bool implicit = basicp->implicit(); + if (!implicit) { + // Since the basicp is not implicit, there should be a rangep indicating the width + width = nodep->basicp()->rangep()->elementsConst(); + } + bool isUnsupportedType = !literal && !implicit; + bool isUnsupportedWidth = literal && width > 64; + if (isUnsupportedType || isUnsupportedWidth) { + v3error("Verilator-configfile: target variable '" + << nodep->name() << "' in '" + << m_currHier << "' must be a supported type!"); + return; + } + AstVar* varp = nodep->cloneTree(false); + varp->name("tmp_" + nodep->name()); + varp->origName("tmp_" + nodep->name()); + varp->trace(true); + if (varp->varType() == VVarType::WIRE) { + varp->varType(VVarType::VAR); + } + setVar(nodep, varp, m_target); + if (string::npos == m_currHier.rfind('.')) { + AstModule* modulep = m_modp->cloneTree(false); + modulep->name(m_modp->name() + "__inst__" + std::to_string(m_instrIdx)); + setInstrModule( + m_modp, modulep, m_currHier); + m_initModp = false; + } + m_foundVarp = true; + } + } + } + }; + + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTOR + //------------------------------------------------------------------------------- + explicit InstrumentationTargetFinder(AstNetlist* nodep) { + const auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (const auto& pair : instrCfg) { + m_netlist = nodep; + m_target = pair.first; + m_initModp = true; + m_currHier = ""; + iterate(nodep); + setProcessed(m_target); + m_foundModp = false; + m_foundCellp = false; + m_foundVarp = false; + m_error = false; + m_targetModp = nullptr; + m_modp = nullptr; + m_instrIdx++; + } + }; + ~InstrumentationTargetFinder() override = default; +}; + +//################################################################################## +// Instrumentation class functions +class InstrumentationFunction final : public VNVisitor { + bool m_assignw = false; // Flag if a assignw exists in the netlist + bool m_addedport = false; // Flag if a port was already added + bool m_addedTask = false; // Flag if a task was already added + bool m_addedFunc = false; // Flag if a function was already added + bool m_interface = false; // Flag if the ParseRef node is part of an interface + int m_pinnum = 0; // Pinnumber for the new Port nodes + string m_targetKey; // Stores the target string from the instrumentation config + string m_task_name; + size_t m_targetIndex = 0; // Index of the target variable in the instrumentation config + AstAlways* m_alwaysp = nullptr; // Stores the added always node + AstAssignW* m_assignwp = nullptr; // Stores the added assignw node + AstGenBlock* m_instGenBlock = nullptr; // Store the GenBlock node for instrumentation hierarchy check + AstTask* m_taskp = nullptr; // // Stores the created task node + AstFunc* m_funcp = nullptr; // Stores the created function node + AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node + AstTaskRef* m_taskrefp = nullptr; // Stores the created taskref node + AstModule* m_current_module = nullptr; // Stores the currenty visited module + AstModule* m_current_module_cell_check = nullptr; // Stores the module node(used by cell visitor) + AstVar* m_tmp_varp = nullptr; // Stores the instrumented variable node + AstVar* m_orig_varp = nullptr; // Stores the original variable node + AstVar* m_orig_varp_instMod = nullptr; // Stores the original variable node in instrumented module node + AstPort* m_orig_portp = nullptr; // Stores the original port node + + // METHODS + //---------------------------------------------------------------------------------- + // Find the relevant instrumentation config in the map corresponding to the given key + const InstrumentationTarget* getInstrCfg(const std::string& key) { + const auto& map = V3Control::getInstrumentationConfigs(); + auto instrCfg = map.find(key); + if (instrCfg != map.end()) { + return &instrCfg->second; + } else { + return nullptr; + } + } + // Get the Cell nodep pointer from the configuration map for the given key + AstCell* getMapEntryCell(const std::string& key) { + if (auto cfg = getInstrCfg(key)) { + return cfg->cellp; + } + return nullptr; + } + // Get the instrumented Module node pointer from the configuration map for the given key + AstModule* getMapEntryInstModule(const std::string& key) { + if (auto cfg = getInstrCfg(key)) { + return cfg->instrModulep; + } + return nullptr; + } + // Get the Module node pointer pointing to the instrumented/original module from the + // configuration map for the given key + AstModule* getMapEntryPointingModule(const std::string& key) { + if (auto cfg = getInstrCfg(key)) { + return cfg->pointingModulep; + } + return nullptr; + } + // Get the instrumented variable node pointer from the configuration map for the given key + AstVar* getMapEntryInstVar(const std::string& key, size_t index) { + if (auto cfg = getInstrCfg(key)) { + const auto& entries = cfg->entries; + if (index < entries.size()) { + return entries[index].instrVarps; + } + } + return nullptr; + } + // Get the original variable node pointer from the configuration map for the given key + AstVar* getMapEntryVar(const std::string& key, size_t index) { + if (auto cfg = getInstrCfg(key)) { + const auto& entries = cfg->entries; + if (index < entries.size()) { + return entries[index].origVarps; + } + } + return nullptr; + } + // Check if the given module node pointer is an instrumented module entry in the configuration + // map for the given key + bool isInstModEntry(AstModule* nodep, const std::string& key) { + const auto& map = V3Control::getInstrumentationConfigs(); + const auto instrCfg = map.find(key); + if (instrCfg != map.end() + && instrCfg->second.instrModulep == nodep) { + return true; + } else { + return false; + } + } + // Check if the given module node pointer is the top module entry in the configuration map + bool isTopModEntry(AstModule* nodep) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (const auto& pair : instrCfg) { + if (nodep == pair.second.topModulep) { return true; } + } + return false; + } + // Check if the given module node pointer is the pointing module entry in the configuration map + bool isPointingModEntry(AstModule* nodep) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (const auto& pair : instrCfg) { + if (nodep == pair.second.pointingModulep) { return true; } + } + return false; + } + // Check if the given module node pointer has already been instrumented/done flag has been set + bool isDone(AstModule* nodep) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (const auto& pair : instrCfg) { + if (nodep == pair.second.instrModulep) { return pair.second.done; } + } + return true; + } + // Check if the multipleCellps flag is set for the given key in the configuration map + bool hasMultiple(const std::string& key) { + const auto& map = V3Control::getInstrumentationConfigs(); + const auto instrCfg = map.find(key); + if (instrCfg != map.end()) { + return instrCfg->second.multipleCellps; + } else { + return false; + } + } + // Get the fault case for the given key in the configuration map + int getMapEntryFaultCase(const std::string& key, size_t index) { + const auto& map = V3Control::getInstrumentationConfigs(); + const auto instrCfg = map.find(key); + if (instrCfg != map.end()) { + const auto& entries = instrCfg->second.entries; + if (index < entries.size()) { + return entries[index].instrID; + } + return -1; // Return -1 if index is out of bounds + } else { + return -1; + } + } + // Get the instrumentation function name for the given key in the configuration map + string getMapEntryFunction(const std::string& key, size_t index) { + const auto& map = V3Control::getInstrumentationConfigs(); + const auto instrCfg = map.find(key); + if (instrCfg != map.end()) { + const auto& entries = instrCfg->second.entries; + if (index < entries.size()) { + return entries[index].instrFunc; + } + return ""; + } else { + return ""; + } + } + // Set the done flag for the given module node pointer in the configuraiton map + void setDone(AstModule* nodep) { + auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (auto& pair : instrCfg) { + if (nodep == pair.second.instrModulep) { pair.second.done = true; } + } + } + AstNode* createDPIInterface(AstModule* nodep, AstVar* orig_varp, const string& task_name) { + AstBasicDType* basicp = nullptr; + if (orig_varp->basicp()->isLiteralType() || orig_varp->basicp()->implicit()) { + int width; + if (orig_varp->basicp()->implicit()) { + // Since Var is implicit set/assume the width as 1 like in V3Width.cpp in the AstVar visitor + width = 1; + } else { + width = orig_varp->basicp()->rangep()->elementsConst(); + } + if (width <= 1) { + basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT}; + } else if (width <= 8) { + basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BYTE}; + } else if (width <= 16) { + basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::SHORTINT}; + } else if (width <= 32) { + basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT}; + } else if (width <= 64) { + basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::LONGINT}; + } + return new AstFunc{nodep->fileline(), m_task_name, nullptr, basicp}; + } else { + return new AstTask{nodep->fileline(), m_task_name, nullptr}; + } + } + + // Visitors + //---------------------------------------------------------------------------------- + + //ASTNETLIST VISITOR FUNCTION: + //Loop over map entries for module nodes and add them to the tree + void visit(AstNetlist* nodep) { + const auto& instrCfg = V3Control::getInstrumentationConfigs(); + for (const auto& pair : instrCfg) { + nodep->addModulesp(pair.second.instrModulep); + m_targetKey = pair.first; + iterateChildren(nodep); + m_assignw = false; + } + } + + //ASTMODULE VISITOR FUNCTION: + //This function is called for each module node in the netlist. + //It checks if the module node is part of the instrumentation configuratio map. + //Depending on the type of the module node (Instrumented, Top, Pointing, or Original), + //it performs different actions: + // - If the module is an instrumented module entry and has not been done, it creates a new + //task for the instrumentation function, adds the temporary variable, and creates a task + //reference to the instrumentation function. + // - If the module is a pointing module or a top module and has no multiple cellps, it checks + //the cell for the target key and counts the pins. This pin count is used in the CELL VISITOR + //FUNCTION to set a siutable pin number for the INSTRUMENT parameter. Look there fore further + //information. + // - If the module is a pointing module and has multiple cellps, it creates a begin block with + //a conditional statement to select between the instrumented and original cell. + // Additionally like in the previous case, the pin count is used to set a suitable pin + //number for the INSTRUMENT parameter.\ Since the cell which need to be edited are located not in + //the original module, but in the pointing/top module, the current_module_cell_check variable is + //set to the module visited by the function and fulfilling this condition. + void visit(AstModule* nodep) { + const InstrumentationTarget& target = V3Control::getInstrumentationConfigs().find( + m_targetKey)->second; + const auto& entries = target.entries; + for (m_targetIndex = 0; m_targetIndex < entries.size(); ++m_targetIndex) { + const auto& entry = entries[m_targetIndex]; + m_tmp_varp = getMapEntryInstVar(m_targetKey, m_targetIndex); + m_orig_varp = getMapEntryVar(m_targetKey, m_targetIndex); + m_task_name = getMapEntryFunction(m_targetKey, m_targetIndex); + if (isInstModEntry(nodep, m_targetKey) && !isDone(nodep)) { + m_current_module = nodep; + + for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { + if (VN_IS(n, Task) && n->name() == m_task_name) { + m_taskp = VN_CAST(n, Task); + m_addedTask = true; + break; + } + if (VN_IS(n, Func) && n->name() == m_task_name) { + m_funcp = VN_CAST(n, Func); + m_addedFunc = true; + break; + } + } + if (!m_addedTask && !m_addedFunc) { + auto m_dpip = createDPIInterface(nodep ,m_orig_varp, m_task_name); + if (VN_IS(m_dpip, Func)) { + m_funcp = VN_CAST(m_dpip, Func); + m_funcp->dpiImport(true); + m_funcp->prototype(true); + nodep->addStmtsp(m_funcp); + } + if (VN_IS(m_dpip, Task)) { + m_taskp = VN_CAST(m_dpip, Task); + m_taskp->dpiImport(true); + m_taskp->prototype(true); + nodep->addStmtsp(m_taskp); + } + } + if (m_orig_varp->direction() == VDirection::INPUT) { + m_tmp_varp->varType(VVarType::VAR); + m_tmp_varp->direction(VDirection::NONE); + m_tmp_varp->trace(true); + } + nodep->addStmtsp(m_tmp_varp); + + if (m_taskp != nullptr) { + m_taskrefp = new AstTaskRef{ + nodep->fileline(), m_task_name, + new AstArg{nodep->fileline(), m_tmp_varp->name(), + new AstVarRef{nodep->fileline(), m_tmp_varp, VAccess::WRITE}}}; + m_taskrefp->taskp(m_taskp); + m_alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, nullptr, nullptr}; + nodep->addStmtsp(m_alwaysp); + } + if (m_funcp != nullptr) { + m_funcrefp = new AstFuncRef{nodep->fileline(), m_funcp, nullptr}; + m_assignwp = new AstAssignW{nodep->fileline(), + new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_tmp_varp->name()}, + m_funcrefp}; + nodep->addStmtsp(m_assignwp); + } + + if (m_targetIndex == entries.size() - 1) { setDone(nodep); } + for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { + if (VN_IS(n, Port)) { + m_pinnum = VN_CAST(n, Port)->pinNum(); + } + } + iterateChildren(nodep); + } else if ((std::count(m_targetKey.begin(), m_targetKey.end(), '.') > 0) + && (isPointingModEntry(nodep) || isTopModEntry(nodep)) + && !hasMultiple(m_targetKey)) { + m_current_module_cell_check = nodep; + AstCell* instCellp = getMapEntryCell(m_targetKey); + for (AstNode* n = instCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + iterateChildren(nodep); + } else if (isPointingModEntry(nodep) && hasMultiple(m_targetKey)) { + m_current_module_cell_check = nodep; + AstCell* instCellp = getMapEntryCell(m_targetKey)->cloneTree(false); + instCellp->modp(getMapEntryInstModule(m_targetKey)); + for (AstNode* n = instCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + m_instGenBlock = new AstGenBlock{nodep->fileline(), "", instCellp, false}; + AstGenIf* genifp = new AstGenIf{ + nodep->fileline(), + new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT"}, + m_instGenBlock, + new AstGenBlock{nodep->fileline(), "", getMapEntryCell(m_targetKey)->cloneTree(false), + false}}; + + nodep->addStmtsp(genifp); + iterateChildren(m_instGenBlock); + iterateChildren(nodep); + } + m_current_module = nullptr; + m_current_module_cell_check = nullptr; + m_alwaysp = nullptr; + m_taskp = nullptr; + m_taskrefp = nullptr; + m_addedTask = false; + m_funcp = nullptr; + m_funcrefp = nullptr; + m_addedFunc = false; + m_addedport = false; + m_instGenBlock = nullptr; + } + m_targetIndex = 0; + } + + //ASTPORT VISITOR FUNCTION: + //When the target variable is an ouput port, this function is called. + //If no port is added yet, two new ports are added to the current module. + //This enabled the instrumentation of the ouput port and link this instrumented port to the + //modules reading from the original port. The idea behind this function is to set the + //instrumented port on the position of the original port in the module and move the original port + //to another pin number. + //This should ensure the linking over the name and the port position in the module should work. + void visit(AstPort* nodep) { + if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::OUTPUT + && nodep->name() == m_orig_varp->name() && !m_addedport) { + m_orig_portp = nodep->cloneTree(false); + nodep->unlinkFrBack(); + nodep->deleteTree(); + m_current_module->addStmtsp( + new AstPort{nodep->fileline(), m_orig_portp->pinNum(), m_tmp_varp->name()}); + m_current_module->addStmtsp( + new AstPort{nodep->fileline(), m_pinnum + 1, m_orig_portp->name()}); + m_addedport = true; + } + } + + //ASTCELL VISITOR FUNCTION: + //This function visits the cell nodes in the module pointing to the instrumented module. + //Depending if hasMultiple is set for the target key, two different actions are performed: + // - If hasMultiple is false, the cell is modified to link to the instrumented module and the + //children are iterated. This ensures that the instrumented mopdule is used in the cell. Also if + //the original variable is an output variable, the children of this cell nodes are visited by the + //ASTPIN VISITOR FUNCTION. + // - If hasMultiple is true, the cell is unlinked from the back and deleted. + // This ensures that the cell is not used anymore in the module, and the conditional + //statment deciding between the instrumented and the original cell can be created/used. A third + //action is performed if the variable beeing instrumented is an ouput variable. In this case the + //children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. + void visit(AstCell* nodep) { + if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) + && nodep == getMapEntryCell(m_targetKey)) { + nodep->modp(getMapEntryInstModule(m_targetKey)); + if (m_orig_varp->direction() == VDirection::OUTPUT) { iterateChildren(nodep); } + } else if (m_current_module_cell_check != nullptr && hasMultiple(m_targetKey) + && nodep == getMapEntryCell(m_targetKey)) { + nodep->unlinkFrBack(); + nodep->deleteTree(); + } else if (m_instGenBlock != nullptr && nodep->modp() == getMapEntryInstModule(m_targetKey) + && m_orig_varp->direction() == VDirection::OUTPUT) { + iterateChildren(nodep); + } else if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::INPUT) { + iterateChildren(nodep); + } + } + + //ASTPIN VISITOR FUNCTION: + //The function is used to change the pin name of the original variable to the instrumented + //variable name. This is done to ensure that the pin is correctly linked to the instrumented + //variable in the cell. + void visit(AstPin* nodep) { + if (nodep->name() == m_orig_varp->name() && m_orig_varp->direction() == VDirection::INPUT) { + iterateChildren(nodep); + } else if (nodep->name() == m_orig_varp->name()) { + nodep->name(m_tmp_varp->name()); + } + } + + //ASTTASK VISITOR FUNCTION: + //The function is used to further specify the task node created at the module visitor. + void visit(AstTask* nodep) { + if (m_addedTask == false && nodep == m_taskp && m_current_module != nullptr) { + AstVar* instrID = nullptr; + AstVar* var_x_task = nullptr; + AstVar* tmp_var_task = nullptr; + + instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; + instrID->direction(VDirection::INPUT); + + var_x_task = m_orig_varp->cloneTree(false); + var_x_task->varType(VVarType::PORT); + var_x_task->direction(VDirection::INPUT); + + tmp_var_task = m_tmp_varp->cloneTree(false); + tmp_var_task->varType(VVarType::PORT); + tmp_var_task->direction(VDirection::OUTPUT); + + nodep->addStmtsp(instrID); + nodep->addStmtsp(var_x_task); + nodep->addStmtsp(tmp_var_task); + } + } + + //ASTFUNC VISITOR FUNCITON: + //The function is used to further specify the function node created at the module visitor. + void visit(AstFunc* nodep) { + if (m_addedFunc == false && nodep == m_funcp && m_current_module != nullptr) { + AstVar* instrID = nullptr; + AstVar* var_x_func = nullptr; + + instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; + instrID->direction(VDirection::INPUT); + + var_x_func = m_orig_varp->cloneTree(false); + var_x_func->varType(VVarType::PORT); + var_x_func->direction(VDirection::INPUT); + + nodep->addStmtsp(instrID); + nodep->addStmtsp(var_x_func); + } + } + + //ASTALWAYS VISITOR FUNCTION: + //The function is used to add the task reference node to the always node and further specify the + //always node. + void visit(AstAlways* nodep) { + if (nodep == m_alwaysp && m_current_module != nullptr) { + AstBegin* newBegin = nullptr; + + m_taskrefp + = new AstTaskRef{nodep->fileline(), m_task_name, nullptr}; + + newBegin = new AstBegin{nodep->fileline(), "", + new AstStmtExpr{nodep->fileline(), m_taskrefp}, false}; + nodep->addStmtsp(newBegin); + } + iterateChildren(nodep); + } + + void visit(AstVar* nodep) { + if (m_current_module != nullptr && nodep->name() == m_orig_varp->name()) { + m_orig_varp_instMod = nodep; + } + } + + //ASTTASKREF VISITOR FUNCTION: + //The function is used to further specify the task reference node called by the always node. + void visit(AstTaskRef* nodep) { + if (nodep == m_taskrefp && m_current_module != nullptr) { + AstConst* constp_id = nullptr; + + constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, + getMapEntryFaultCase(m_targetKey, m_targetIndex)}; + + AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + + nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); + nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); + nodep->addPinsp(new AstArg{ + nodep->fileline(), "", + new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_tmp_varp->name()}}); + m_orig_varp_instMod = nullptr; + } + } + + //ASTFUNCREF VISITOR FUNCTION: + //The function is used to further specify the function reference node called by the assignw node + void visit(AstFuncRef* nodep) { + if (nodep == m_funcrefp && m_current_module != nullptr) { + AstConst* constp_id = nullptr; + + constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, + getMapEntryFaultCase(m_targetKey, m_targetIndex)}; + + AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + + nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); + nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); + m_orig_varp_instMod = nullptr; + } + } + + //ASTASSIGNW VISITOR FUNCTION: + //Sets the m_assignw flag to true if the current module is not null. + //Necessary for the AstParseRef visitor function to determine if the current node is part of an + //assignment. + void visit(AstAssignW* nodep) { + if (m_current_module != nullptr) { + if (nodep != m_assignwp) { + m_assignw = true; + } + iterateChildren(nodep); + } + m_assignw = false; + m_interface = false; + } + + // These two function are used to circumvent the instrumentation of ParseRef nodes for interfaces + void visit(AstDot* nodep) { + if (m_current_module != nullptr) { + m_interface = true; + } + } + void visit(AstReplicate* nodep) { + if (m_current_module != nullptr) { + m_interface = true; + } + } + + //ASTPARSE REF VISITOR FUNCTION: + //The function is used to change the parseref nodes to link to the instrumented variable instead + //of the original variable. Depending on the direction of the original variable, different + //actions are performed: + // - If the original variable is not an output variable and the assignment is true, the + //parseref node is changed to link to the instrumented variable. This ensures that the + //instrumented variable is used in the assignment. + // - If the original variable is an input variable, every parseref node is changed to link to + //the instrumented variable. This ensures that the instrumented variable is used as the new + //input. + void visit(AstParseRef* nodep) { + if (m_current_module != nullptr && m_orig_varp != nullptr + && nodep->name() == m_orig_varp->name()) { + if (m_assignw && !m_interface && m_orig_varp->direction() != VDirection::OUTPUT) { + nodep->name(m_tmp_varp->name()); + } else if (m_orig_varp->direction() == VDirection::INPUT) { + nodep->name(m_tmp_varp->name()); + } + } + } + + //----------------- + void visit(AstNode* nodep) override { iterateChildren(nodep); } + +public: + // CONSTRUCTORS + explicit InstrumentationFunction(AstNetlist* nodep) { iterate(nodep); } + ~InstrumentationFunction() override = default; +}; + +//################################################################################## +// Instrumentation class functions + +// Function to find instrumentation targets and additional information for the instrumentation +// process +void V3Instrumentation::findTargets(AstNetlist* nodep) { + UINFO(2, __FUNCTION__ << ": " << endl); + { InstrumentationTargetFinder{nodep}; } + V3Global::dumpCheckGlobalTree("instrumentationFinder", 0, dumpTreeEitherLevel() >= 3); +} + +// Function for the actual instrumentation process +void V3Instrumentation::instrument(AstNetlist* nodep) { + UINFO(2, __FUNCTION__ << ": " << endl); + { InstrumentationFunction{nodep}; } + V3Global::dumpCheckGlobalTree("instrumentationFunction", 0, dumpTreeEitherLevel() >= 3); +} diff --git a/src/V3Instrumentation.h b/src/V3Instrumentation.h new file mode 100644 index 000000000..148672203 --- /dev/null +++ b/src/V3Instrumentation.h @@ -0,0 +1,33 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: +// +// 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 +// +//************************************************************************* + +#ifndef VERILATOR_V3INSTRUMENTATION_H_ +#define VERILATOR_V3INSTRUMENTATION_H_ + +#include "config_build.h" +#include "verilatedos.h" + +class AstNetlist; + +//========================================================================= + +class V3Instrumentation final { +public: + static void findTargets(AstNetlist* nodep) VL_MT_DISABLED; + static void instrument(AstNetlist* nodep) VL_MT_DISABLED; +}; + +#endif // Guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 6a300f141..21429c9ab 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1946,6 +1946,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, addIncDirUser(parseFileArg(optdir, string{valp})); }); + DECL_OPTION("-instrument", OnOff, &m_instrument); + parser.finalize(); for (int i = 0; i < argc;) { diff --git a/src/V3Options.h b/src/V3Options.h index 476ea2289..dbafb4a52 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -308,6 +308,7 @@ private: bool m_waiverMultiline = false; // main switch: --waiver-multiline bool m_xInitialEdge = false; // main switch: --x-initial-edge bool m_xmlOnly = false; // main switch: --xml-only + bool m_instrument = false; // main switch: --instrument int m_buildJobs = -1; // main switch: --build-jobs, -j int m_coverageExprMax = 32; // main switch: --coverage-expr-max @@ -584,6 +585,7 @@ public: bool xmlOnly() const { return m_xmlOnly; } bool serializeOnly() const { return m_xmlOnly || m_jsonOnly; } bool topIfacesSupported() const { return lintOnly() && !hierarchical(); } + bool instrument() const { return m_instrument; } int buildJobs() const VL_MT_SAFE { return m_buildJobs; } int convergeLimit() const { return m_convergeLimit; } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 139c86fef..ad17ed494 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -64,6 +64,7 @@ #include "V3HierBlock.h" #include "V3Inline.h" #include "V3Inst.h" +#include "V3Instrumentation.h" #include "V3Interface.h" #include "V3Life.h" #include "V3LifePost.h" @@ -152,6 +153,14 @@ static void process() { v3Global.vlExit(0); } + // Instrument Design with the configurations given in .vlt file + if (v3Global.opt.instrument()) { + v3Global.dpi(true); + V3Instrumentation::findTargets(v3Global.rootp()); + V3Error::abortIfErrors(); + V3Instrumentation::instrument(v3Global.rootp()); + } + // Convert parseref's to varrefs, and other directly post parsing fixups V3LinkParse::linkParse(v3Global.rootp()); // Cross-link signal names diff --git a/src/verilog.l b/src/verilog.l index 80969768b..1aec846cc 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -123,6 +123,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "hier_params" { FL; return yVLT_HIER_PARAMS; } "hier_workers" { FL; return yVLT_HIER_WORKERS; } "inline" { FL; return yVLT_INLINE; } + "instrument" { FL; return yVLT_INSTRUMENT; } "isolate_assignments" { FL; return yVLT_ISOLATE_ASSIGNMENTS; } "lint_off" { FL; return yVLT_LINT_OFF; } "lint_on" { FL; return yVLT_LINT_ON; } @@ -149,6 +150,8 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} -?"-file" { FL; return yVLT_D_FILE; } -?"-function" { FL; return yVLT_D_FUNCTION; } -?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; } + -?"-id" { FL; return yVLT_D_ID; } + -?"-instance" { FL; return yVLT_D_INSTANCE; } -?"-levels" { FL; return yVLT_D_LEVELS; } -?"-lines" { FL; return yVLT_D_LINES; } -?"-match" { FL; return yVLT_D_MATCH; } @@ -159,6 +162,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} -?"-port" { FL; return yVLT_D_PORT; } -?"-rule" { FL; return yVLT_D_RULE; } -?"-scope" { FL; return yVLT_D_SCOPE; } + -?"-target" { FL; return yVLT_D_TARGET; } -?"-task" { FL; return yVLT_D_TASK; } -?"-var" { FL; return yVLT_D_VAR; } -?"-workers" { FL; return yVLT_D_WORKERS; } diff --git a/src/verilog.y b/src/verilog.y index a94a9046f..082996b09 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -250,6 +250,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_HIER_PARAMS "hier_params" %token yVLT_HIER_WORKERS "hier_workers" %token yVLT_INLINE "inline" +%token yVLT_INSTRUMENT "instrument" %token yVLT_ISOLATE_ASSIGNMENTS "isolate_assignments" %token yVLT_LINT_OFF "lint_off" %token yVLT_LINT_ON "lint_on" @@ -276,6 +277,8 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" %token yVLT_D_HIER_DPI "--hier-dpi" +%token yVLT_D_ID "--id" +%token yVLT_D_INSTANCE "--instance" %token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" @@ -286,6 +289,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_D_PORT "--port" %token yVLT_D_RULE "--rule" %token yVLT_D_SCOPE "--scope" +%token yVLT_D_TARGET "--target" %token yVLT_D_TASK "--task" %token yVLT_D_VAR "--var" %token yVLT_D_WORKERS "--workers" @@ -8009,6 +8013,8 @@ vltItem: { /* Historical, now has no effect */ } | vltInlineFront vltDModuleE vltDFTaskE { V3Control::addInline($1, *$2, *$3, $1); } + | yVLT_INSTRUMENT yVLT_D_MODEL yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING + { V3Control::addInstrumentationConfigs($1, *$3, $5->toSInt(), *$7); } | yVLT_COVERAGE_BLOCK_OFF vltDFile { V3Control::addCoverageBlockOff(*$2, 0); } | yVLT_COVERAGE_BLOCK_OFF vltDFile yVLT_D_LINES yaINTNUM From faf67f45f69d26fc328fbefdb6fc00e13e51e26d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 1 Oct 2025 13:01:48 +0200 Subject: [PATCH 02/20] feat: Add V3DumpSignals-Pass --- src/CMakeLists.txt | 2 + src/Makefile_obj.in | 1 + src/V3DumpSignals.cpp | 117 ++++++++++++++++++++++++++++++++++++++++++ src/V3DumpSignals.h | 32 ++++++++++++ src/V3Options.cpp | 1 + src/V3Options.h | 2 + src/Verilator.cpp | 5 ++ 7 files changed, 160 insertions(+) create mode 100644 src/V3DumpSignals.cpp create mode 100644 src/V3DumpSignals.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 2c3b9f3e2..acc2eaf18 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,6 +84,7 @@ set(HEADERS V3DfgPeepholePatterns.h V3DfgVertices.h V3DiagSarif.h + V3DumpSignals.h V3DupFinder.h V3EmitC.h V3EmitCBase.h @@ -251,6 +252,7 @@ set(COMMON_SOURCES V3DfgRegularize.cpp V3DfgSynthesize.cpp V3DiagSarif.cpp + V3DumpSignals.cpp V3DupFinder.cpp V3EmitCBase.cpp V3EmitCConstPool.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 16725d2fd..939aee11b 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -269,6 +269,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3DfgRegularize.o \ V3DfgSynthesize.o \ V3DiagSarif.o \ + V3DumpSignals.o \ V3DupFinder.o \ V3EmitCMain.o \ V3EmitCMake.o \ diff --git a/src/V3DumpSignals.cpp b/src/V3DumpSignals.cpp new file mode 100644 index 000000000..bb792719b --- /dev/null +++ b/src/V3DumpSignals.cpp @@ -0,0 +1,117 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: +// +// 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 "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT + +#include "V3DumpSignals.h" + +#include "V3Global.h" + +#include +#include +#include + +VL_DEFINE_DEBUG_FUNCTIONS; +V3Global v3global; + +class DumpSignals final : public VNVisitor { + bool m_firstModuleNode = true; + bool m_foundCell = false; + string m_currHier; + std::ofstream m_signalFile; + + // Methods + void processVar(AstVar* varp) { + if (varp->basicp() && varp->basicp()->name() != "") { + bool hasRangep = varp->basicp()->rangep() != nullptr; + bool isSized = varp->basicp()->widthSized(); + if (hasRangep) { + std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(varp->basicp()->rangep()->elementsConst()) + "]"; + m_signalFile << varHier << "\n"; + } else { + if (varp->basicp()->implicit()){ + // Since Var is implicit set the width to 1 like in V3Width.cpp in the AstVar visitor + std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(1) + "]"; + m_signalFile << varHier << "\n"; + } else { + std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(varp->basicp()->width()) + "]"; + m_signalFile << varHier << "\n"; + } + } + } + } + + void processChildrenNode(AstNode* nodep) { + for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { + if (VN_IS(n, Var)) { + AstVar* varp = VN_AS(n, Var); + if (!varp->isParam() && !varp->isGenVar() && !varp->isIfaceRef() && !varp->isIfaceParent()) { + processVar(varp); + } + } else if (VN_IS(n, Cell)) { + if (VN_IS(VN_AS(n, Cell)->modp(), Module)) { + m_foundCell = true; + std::string oldHier = m_currHier; + m_currHier += n->name() + "."; + diveIntoCellModp(VN_AS(n, Cell)->modp()); + m_currHier = oldHier; + } + } + } + } + + void diveIntoCellModp(AstNodeModule* modp) { + processChildrenNode(modp); + } + + // VISITORS + void visit(AstModule* nodep) override { + if (m_firstModuleNode) { + m_currHier = nodep->name() + "."; + processChildrenNode(nodep); + m_firstModuleNode = false; + } + } + + //----------------- + void visit(AstNode* nodep) override { + iterateChildren(nodep); + } + +public: + explicit DumpSignals(AstNetlist* nodep) { + std::string filePath = v3global.opt.hierTopDataDir() + "/signalDump.log"; + m_signalFile.open(filePath); + iterate(nodep); + } + + ~DumpSignals() override { + if (m_signalFile.is_open()) { + m_signalFile.close(); + } + } +}; + +//################################################################################## +// DumpSignals class functions + +void V3DumpSignals::dumpSignals(AstNetlist* nodep) { + UINFO(2, __FUNCTION__ << ": " << endl); + { DumpSignals{nodep}; } + V3Global::dumpCheckGlobalTree("dumpSignals", 0, dumpTreeEitherLevel() >= 3); +} diff --git a/src/V3DumpSignals.h b/src/V3DumpSignals.h new file mode 100644 index 000000000..baf24f06c --- /dev/null +++ b/src/V3DumpSignals.h @@ -0,0 +1,32 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +//************************************************************************* +// DESCRIPTION: Verilator: +// +// 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 +// +//************************************************************************* + +#ifndef VERILATOR_V3DUMPSIGNALS_H_ +#define VERILATOR_V3DUMPSIGNALS_H_ + +#include "config_build.h" +#include "verilatedos.h" + +class AstNetlist; + +//========================================================================= + +class V3DumpSignals final { +public: + static void dumpSignals(AstNetlist* nodep) VL_MT_DISABLED; +}; + +#endif // Guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 21429c9ab..78310680c 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1947,6 +1947,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-instrument", OnOff, &m_instrument); + DECL_OPTION("-dump-signals", OnOff, &m_dumpSignals); parser.finalize(); diff --git a/src/V3Options.h b/src/V3Options.h index dbafb4a52..39ac5fa85 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -250,6 +250,7 @@ private: bool m_decorationNodes = false; // main switch: --decoration=nodes bool m_diagnosticsSarif = false; // main switch: --diagnostics-sarif bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only + bool m_dumpSignals = false; // main switch: --dump-signals bool m_emitAccessors = false; // main switch: --emit-accessors bool m_exe = false; // main switch: --exe bool m_flatten = false; // main switch: --flatten @@ -529,6 +530,7 @@ public: bool diagnosticsSarif() const VL_MT_SAFE { return m_diagnosticsSarif; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); } + bool dumpSignals() const { return m_dumpSignals; } bool dumpTreeDot() const { return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index ad17ed494..de0b3f2a1 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -45,6 +45,7 @@ #include "V3Descope.h" #include "V3DfgOptimizer.h" #include "V3DiagSarif.h" +#include "V3DumpSignals.h" #include "V3EmitC.h" #include "V3EmitCMain.h" #include "V3EmitCMake.h" @@ -153,6 +154,10 @@ static void process() { v3Global.vlExit(0); } + if (v3Global.opt.dumpSignals()) { + V3DumpSignals::dumpSignals(v3Global.rootp()); + } + // Instrument Design with the configurations given in .vlt file if (v3Global.opt.instrument()) { v3Global.dpi(true); From 3500b6d215cb044b069a3d5f5bc8e70179045435 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 1 Oct 2025 13:05:02 +0200 Subject: [PATCH 03/20] test: Add regression tests for instrumentation --- test_regress/t/t_instrumentation.cpp | 42 ++++++++++ test_regress/t/t_instrumentation.out | 101 ++++++++++++++++++++++++ test_regress/t/t_instrumentation.py | 27 +++++++ test_regress/t/t_instrumentation.v | 37 +++++++++ test_regress/t/t_instrumentation.vlt | 9 +++ test_regress/t/t_instrumentationDPI.cpp | 28 +++++++ 6 files changed, 244 insertions(+) create mode 100644 test_regress/t/t_instrumentation.cpp create mode 100644 test_regress/t/t_instrumentation.out create mode 100755 test_regress/t/t_instrumentation.py create mode 100644 test_regress/t/t_instrumentation.v create mode 100644 test_regress/t/t_instrumentation.vlt create mode 100644 test_regress/t/t_instrumentationDPI.cpp diff --git a/test_regress/t/t_instrumentation.cpp b/test_regress/t/t_instrumentation.cpp new file mode 100644 index 000000000..d88813e15 --- /dev/null +++ b/test_regress/t/t_instrumentation.cpp @@ -0,0 +1,42 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2024 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +#include +#include + +#include + +#include VM_PREFIX_INCLUDE + +unsigned long long main_time = 0; +double sc_time_stamp() { return main_time; } + +int main(int argc, char** argv) { + Verilated::debug(0); + Verilated::commandArgs(argc, argv); + + std::unique_ptr top{new VM_PREFIX{"top"}}; + + std::ofstream logFile("obj_vlt/t_instrumentation/simulation_output.log"); + if (!logFile.is_open()) { + printf("Error: Could not open log file\n"); + return 1; + } + + while (main_time <= 100) { + top->eval(); + logFile << "$time: " << main_time << " | " + << "Output outa: " << static_cast(top->outa) << " | " + << "Output outb: " << static_cast(top->outb) << std::endl; + ++main_time; + } + top->final(); + top.reset(); + printf("*-* All Finished *-*\n"); + return 0; +} diff --git a/test_regress/t/t_instrumentation.out b/test_regress/t/t_instrumentation.out new file mode 100644 index 000000000..7ba39071f --- /dev/null +++ b/test_regress/t/t_instrumentation.out @@ -0,0 +1,101 @@ +$time: 0 | Output outa: 0 | Output outb: 50 +$time: 1 | Output outa: 0 | Output outb: 50 +$time: 2 | Output outa: 0 | Output outb: 50 +$time: 3 | Output outa: 0 | Output outb: 50 +$time: 4 | Output outa: 0 | Output outb: 50 +$time: 5 | Output outa: 0 | Output outb: 50 +$time: 6 | Output outa: 0 | Output outb: 50 +$time: 7 | Output outa: 0 | Output outb: 50 +$time: 8 | Output outa: 0 | Output outb: 50 +$time: 9 | Output outa: 0 | Output outb: 50 +$time: 10 | Output outa: 0 | Output outb: 50 +$time: 11 | Output outa: 0 | Output outb: 50 +$time: 12 | Output outa: 0 | Output outb: 50 +$time: 13 | Output outa: 0 | Output outb: 50 +$time: 14 | Output outa: 0 | Output outb: 50 +$time: 15 | Output outa: 0 | Output outb: 50 +$time: 16 | Output outa: 0 | Output outb: 50 +$time: 17 | Output outa: 0 | Output outb: 50 +$time: 18 | Output outa: 0 | Output outb: 50 +$time: 19 | Output outa: 0 | Output outb: 50 +$time: 20 | Output outa: 0 | Output outb: 50 +$time: 21 | Output outa: 0 | Output outb: 50 +$time: 22 | Output outa: 0 | Output outb: 50 +$time: 23 | Output outa: 0 | Output outb: 50 +$time: 24 | Output outa: 0 | Output outb: 50 +$time: 25 | Output outa: 0 | Output outb: 50 +$time: 26 | Output outa: 0 | Output outb: 50 +$time: 27 | Output outa: 0 | Output outb: 50 +$time: 28 | Output outa: 0 | Output outb: 50 +$time: 29 | Output outa: 0 | Output outb: 50 +$time: 30 | Output outa: 0 | Output outb: 50 +$time: 31 | Output outa: 0 | Output outb: 50 +$time: 32 | Output outa: 0 | Output outb: 50 +$time: 33 | Output outa: 0 | Output outb: 50 +$time: 34 | Output outa: 0 | Output outb: 50 +$time: 35 | Output outa: 0 | Output outb: 50 +$time: 36 | Output outa: 0 | Output outb: 50 +$time: 37 | Output outa: 0 | Output outb: 50 +$time: 38 | Output outa: 0 | Output outb: 50 +$time: 39 | Output outa: 0 | Output outb: 50 +$time: 40 | Output outa: 0 | Output outb: 50 +$time: 41 | Output outa: 0 | Output outb: 50 +$time: 42 | Output outa: 0 | Output outb: 50 +$time: 43 | Output outa: 0 | Output outb: 50 +$time: 44 | Output outa: 0 | Output outb: 50 +$time: 45 | Output outa: 0 | Output outb: 50 +$time: 46 | Output outa: 0 | Output outb: 50 +$time: 47 | Output outa: 0 | Output outb: 50 +$time: 48 | Output outa: 0 | Output outb: 50 +$time: 49 | Output outa: 0 | Output outb: 50 +$time: 50 | Output outa: 0 | Output outb: 50 +$time: 51 | Output outa: 0 | Output outb: 50 +$time: 52 | Output outa: 0 | Output outb: 50 +$time: 53 | Output outa: 0 | Output outb: 50 +$time: 54 | Output outa: 0 | Output outb: 50 +$time: 55 | Output outa: 0 | Output outb: 50 +$time: 56 | Output outa: 0 | Output outb: 50 +$time: 57 | Output outa: 0 | Output outb: 50 +$time: 58 | Output outa: 0 | Output outb: 50 +$time: 59 | Output outa: 0 | Output outb: 50 +$time: 60 | Output outa: 0 | Output outb: 50 +$time: 61 | Output outa: 0 | Output outb: 50 +$time: 62 | Output outa: 0 | Output outb: 50 +$time: 63 | Output outa: 0 | Output outb: 50 +$time: 64 | Output outa: 0 | Output outb: 50 +$time: 65 | Output outa: 0 | Output outb: 50 +$time: 66 | Output outa: 0 | Output outb: 50 +$time: 67 | Output outa: 0 | Output outb: 50 +$time: 68 | Output outa: 0 | Output outb: 50 +$time: 69 | Output outa: 0 | Output outb: 50 +$time: 70 | Output outa: 0 | Output outb: 50 +$time: 71 | Output outa: 0 | Output outb: 50 +$time: 72 | Output outa: 0 | Output outb: 50 +$time: 73 | Output outa: 0 | Output outb: 50 +$time: 74 | Output outa: 0 | Output outb: 50 +$time: 75 | Output outa: 0 | Output outb: 50 +$time: 76 | Output outa: 0 | Output outb: 50 +$time: 77 | Output outa: 0 | Output outb: 50 +$time: 78 | Output outa: 0 | Output outb: 50 +$time: 79 | Output outa: 0 | Output outb: 50 +$time: 80 | Output outa: 0 | Output outb: 50 +$time: 81 | Output outa: 0 | Output outb: 50 +$time: 82 | Output outa: 0 | Output outb: 50 +$time: 83 | Output outa: 0 | Output outb: 50 +$time: 84 | Output outa: 0 | Output outb: 50 +$time: 85 | Output outa: 0 | Output outb: 50 +$time: 86 | Output outa: 0 | Output outb: 50 +$time: 87 | Output outa: 0 | Output outb: 50 +$time: 88 | Output outa: 0 | Output outb: 50 +$time: 89 | Output outa: 0 | Output outb: 50 +$time: 90 | Output outa: 0 | Output outb: 50 +$time: 91 | Output outa: 0 | Output outb: 50 +$time: 92 | Output outa: 0 | Output outb: 50 +$time: 93 | Output outa: 0 | Output outb: 50 +$time: 94 | Output outa: 0 | Output outb: 50 +$time: 95 | Output outa: 0 | Output outb: 50 +$time: 96 | Output outa: 0 | Output outb: 50 +$time: 97 | Output outa: 0 | Output outb: 50 +$time: 98 | Output outa: 0 | Output outb: 50 +$time: 99 | Output outa: 0 | Output outb: 50 +$time: 100 | Output outa: 0 | Output outb: 50 diff --git a/test_regress/t/t_instrumentation.py b/test_regress/t/t_instrumentation.py new file mode 100755 index 000000000..83bcaed16 --- /dev/null +++ b/test_regress/t/t_instrumentation.py @@ -0,0 +1,27 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 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 + +import vltest_bootstrap + +test.scenarios('simulator') +test.top_filename = "t/t_instrumentation.v" + +sim_filename = "t/" + test.name + ".cpp" +dpi_filename = "t/t_instrumentationDPI.cpp" +vlt_filename = "t/" + test.name + ".vlt" +log_filename = "obj_vlt/t_instrumentation/simulation_output.log" + +test.compile(make_top_shell=False, + make_main=False, + v_flags2=["--trace --exe --instrument", sim_filename, vlt_filename, dpi_filename]) +test.execute() + +test.files_identical(log_filename, test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_instrumentation.v b/test_regress/t/t_instrumentation.v new file mode 100644 index 000000000..674f8293e --- /dev/null +++ b/test_regress/t/t_instrumentation.v @@ -0,0 +1,37 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2012 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +module top_module( + output logic [7:0] outa, + output logic [7:0] outb +); + logic [7:0] in1a = 8'd5; + logic [7:0] in2a = 8'd10; + + logic [7:0] in1b = 8'd20; + logic [7:0] in2b = 8'd30; + + module_a a1 (.in1(in1a), .in2(in2a), .out(outa)); + module_a a2 (.in1(in1b), .in2(in2b), .out(outb)); +endmodule + +module module_a( + input logic [7:0] in1, + input logic [7:0] in2, + output logic [7:0] out +); + module_b b1 (.in1(in1), .in2(in2), .out(out)); +endmodule + +module module_b ( + input logic [7:0] in1, + input logic [7:0] in2, + output logic [7:0] out +); + always_comb begin + out = in1 + in2; + end +endmodule diff --git a/test_regress/t/t_instrumentation.vlt b/test_regress/t/t_instrumentation.vlt new file mode 100644 index 000000000..e96535c09 --- /dev/null +++ b/test_regress/t/t_instrumentation.vlt @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`verilator_config + +instrument -model "instrument_var" -id 0 -target "top_module.a1.b1.out" diff --git a/test_regress/t/t_instrumentationDPI.cpp b/test_regress/t/t_instrumentationDPI.cpp new file mode 100644 index 000000000..d74027ab9 --- /dev/null +++ b/test_regress/t/t_instrumentationDPI.cpp @@ -0,0 +1,28 @@ +// -*- mode: C++; c-file-style: "cc-mode" -*- +// +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +#include + +#include +#include + +extern "C" short instrument_var(int id, const svLogic *x) { + switch (id) + { + case 0: + return 0; + case 1: + // Stuck at 1 Fault Injection + return 1; + case 2: + // Inverter/Bit flip Fault injection (provisional) + return !x; + default: + return *x; + } +} From 121f675ed592471e6369b8e298175dbb05a389b8 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 1 Oct 2025 12:26:14 +0000 Subject: [PATCH 04/20] Apply 'make format' --- src/V3Control.cpp | 18 +- src/V3Control.h | 3 +- src/V3DumpSignals.cpp | 80 ++-- src/V3Instrumentation.cpp | 463 +++++++++++------------- src/Verilator.cpp | 4 +- test_regress/t/t_instrumentationDPI.cpp | 11 +- 6 files changed, 267 insertions(+), 312 deletions(-) diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 9e354abb7..605d9dbef 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -656,18 +656,21 @@ public: const string& target) { // Error MSG if the instrumentation of the top module is not possible if ((std::count(target.begin(), target.end(), '.') < 2)) { - v3fatal("In .vlt defined target tries to instrument the highest MODULE, is not possible!" - " ... Target string: " << target); + v3fatal( + "In .vlt defined target tries to instrument the highest MODULE, is not possible!" + " ... Target string: " + << target); } - // Implement custom iterator to remove the last part of the target and insert it into the vector of the map - // If the target string is the same as one already in the map, push the var to the vector + // Implement custom iterator to remove the last part of the target and insert it into the + // vector of the map If the target string is the same as one already in the map, push the + // var to the vector auto [prefix, varTarget] = splitPrefixAndVar(target); InstrumentationEntry entry{instrID, instrFunction, varTarget}; auto it = m_instrCfg.find(prefix); if (it != m_instrCfg.end()) { it->second.entries.push_back(entry); } else { - // Create a new entry in the map + // Create a new entry in the map InstrumentationTarget newTarget; newTarget.entries.push_back(entry); m_instrCfg[prefix] = std::move(newTarget); @@ -735,7 +738,7 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) { } void V3Control::addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, - int instrID, const string& target) { + int instrID, const string& target) { V3ControlResolver::s().addInstrumentationConfigs(fl, instrumentationfunc, instrID, target); } @@ -900,7 +903,8 @@ int V3Control::getHierWorkers(const string& model) { FileLine* V3Control::getHierWorkersFileLine(const string& model) { return V3ControlResolver::s().getHierWorkersFileLine(model); } -std::map& V3Control::getInstrumentationConfigs() { +std::map& +V3Control::getInstrumentationConfigs() { return V3ControlResolver::s().getInstrumentationConfigs(); } uint64_t V3Control::getProfileData(const string& hierDpi) { diff --git a/src/V3Control.h b/src/V3Control.h index e95978536..7528ac3ae 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -71,7 +71,8 @@ public: static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); static void addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, int instrID, const string& target); - static std::map& getInstrumentationConfigs(); + static std::map& + getInstrumentationConfigs(); static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, diff --git a/src/V3DumpSignals.cpp b/src/V3DumpSignals.cpp index bb792719b..c0feeabf3 100644 --- a/src/V3DumpSignals.cpp +++ b/src/V3DumpSignals.cpp @@ -22,8 +22,8 @@ #include "V3Global.h" -#include #include +#include #include VL_DEFINE_DEBUG_FUNCTIONS; @@ -38,46 +38,52 @@ class DumpSignals final : public VNVisitor { // Methods void processVar(AstVar* varp) { if (varp->basicp() && varp->basicp()->name() != "") { - bool hasRangep = varp->basicp()->rangep() != nullptr; - bool isSized = varp->basicp()->widthSized(); - if (hasRangep) { - std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(varp->basicp()->rangep()->elementsConst()) + "]"; - m_signalFile << varHier << "\n"; - } else { - if (varp->basicp()->implicit()){ - // Since Var is implicit set the width to 1 like in V3Width.cpp in the AstVar visitor - std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(1) + "]"; - m_signalFile << varHier << "\n"; - } else { - std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + std::to_string(varp->basicp()->width()) + "]"; - m_signalFile << varHier << "\n"; - } - } - } + bool hasRangep = varp->basicp()->rangep() != nullptr; + bool isSized = varp->basicp()->widthSized(); + if (hasRangep) { + std::string varHier + = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" + + std::to_string(varp->basicp()->rangep()->elementsConst()) + "]"; + m_signalFile << varHier << "\n"; + } else { + if (varp->basicp()->implicit()) { + // Since Var is implicit set the width to 1 like in V3Width.cpp in the AstVar + // visitor + std::string varHier = m_currHier + varp->name() + " : Type[" + + varp->basicp()->name() + "] Width[" + std::to_string(1) + + "]"; + m_signalFile << varHier << "\n"; + } else { + std::string varHier = m_currHier + varp->name() + " : Type[" + + varp->basicp()->name() + "] Width[" + + std::to_string(varp->basicp()->width()) + "]"; + m_signalFile << varHier << "\n"; + } + } + } } void processChildrenNode(AstNode* nodep) { for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { - if (VN_IS(n, Var)) { - AstVar* varp = VN_AS(n, Var); - if (!varp->isParam() && !varp->isGenVar() && !varp->isIfaceRef() && !varp->isIfaceParent()) { - processVar(varp); - } - } else if (VN_IS(n, Cell)) { - if (VN_IS(VN_AS(n, Cell)->modp(), Module)) { - m_foundCell = true; - std::string oldHier = m_currHier; - m_currHier += n->name() + "."; - diveIntoCellModp(VN_AS(n, Cell)->modp()); - m_currHier = oldHier; - } + if (VN_IS(n, Var)) { + AstVar* varp = VN_AS(n, Var); + if (!varp->isParam() && !varp->isGenVar() && !varp->isIfaceRef() + && !varp->isIfaceParent()) { + processVar(varp); + } + } else if (VN_IS(n, Cell)) { + if (VN_IS(VN_AS(n, Cell)->modp(), Module)) { + m_foundCell = true; + std::string oldHier = m_currHier; + m_currHier += n->name() + "."; + diveIntoCellModp(VN_AS(n, Cell)->modp()); + m_currHier = oldHier; } } + } } - void diveIntoCellModp(AstNodeModule* modp) { - processChildrenNode(modp); - } + void diveIntoCellModp(AstNodeModule* modp) { processChildrenNode(modp); } // VISITORS void visit(AstModule* nodep) override { @@ -89,9 +95,7 @@ class DumpSignals final : public VNVisitor { } //----------------- - void visit(AstNode* nodep) override { - iterateChildren(nodep); - } + void visit(AstNode* nodep) override { iterateChildren(nodep); } public: explicit DumpSignals(AstNetlist* nodep) { @@ -101,9 +105,7 @@ public: } ~DumpSignals() override { - if (m_signalFile.is_open()) { - m_signalFile.close(); - } + if (m_signalFile.is_open()) { m_signalFile.close(); } } }; diff --git a/src/V3Instrumentation.cpp b/src/V3Instrumentation.cpp index 8d0dc523a..17c9ba047 100644 --- a/src/V3Instrumentation.cpp +++ b/src/V3Instrumentation.cpp @@ -41,18 +41,19 @@ VL_DEFINE_DEBUG_FUNCTIONS; //################################################################################## // Instrumentation class finder class InstrumentationTargetFinder final : public VNVisitor { - AstNetlist* m_netlist = nullptr; // Enable traversing from the beginning if the visitor is to deep - AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node - AstModule* m_modp = nullptr; // Stores the current modulep the visitor is looking at - AstModule* m_targetModp = nullptr; // Stores the targeted modulep - bool m_error = false; // Displays if there was already an error message earlier - bool m_foundCellp = false; // If the visitor found the relevant instance - bool m_foundModp = false; // If the visitor found the relevant model - bool m_foundVarp = false; // If the visitor found the relevant variable - bool m_initModp = true; // If the visitor is in the first module node of the netlist + AstNetlist* m_netlist + = nullptr; // Enable traversing from the beginning if the visitor is to deep + AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node + AstModule* m_modp = nullptr; // Stores the current modulep the visitor is looking at + AstModule* m_targetModp = nullptr; // Stores the targeted modulep + bool m_error = false; // Displays if there was already an error message earlier + bool m_foundCellp = false; // If the visitor found the relevant instance + bool m_foundModp = false; // If the visitor found the relevant model + bool m_foundVarp = false; // If the visitor found the relevant variable + bool m_initModp = true; // If the visitor is in the first module node of the netlist size_t m_instrIdx = 0; - string m_currHier; // Stores the current hierarchy of the visited nodes (Module, Cell, Var) - string m_target; // Stores the currently visited target string from the config map + string m_currHier; // Stores the current hierarchy of the visited nodes (Module, Cell, Var) + string m_target; // Stores the currently visited target string from the config map // METHODS //---------------------------------------------------------------------------------- @@ -63,7 +64,7 @@ class InstrumentationTargetFinder final : public VNVisitor { return nullptr; } // Helper function to compare the target string starts with the given prefix - bool cmpPrefix(const string& prefix, const string& target ) { + bool cmpPrefix(const string& prefix, const string& target) { if (target.compare(0, prefix.size(), prefix) == 0 && (target.size() == prefix.size() || target[prefix.size()] == '.')) { return true; @@ -73,18 +74,14 @@ class InstrumentationTargetFinder final : public VNVisitor { // Helper function to check if a parameter was already added to the tree previously bool hasParam(AstModule* modp) { for (AstNode* n = modp->op2p(); n; n = n->nextp()) { - if (n->name() == "INSTRUMENT") { - return true; - } + if (n->name() == "INSTRUMENT") { return true; } } return false; } // Helper function to check if a pin was already added to the tree previously bool hasPin(AstCell* cellp) { for (AstNode* n = cellp->paramsp(); n; n = n->nextp()) { - if (n->name() == "INSTRUMENT") { - return true; - } + if (n->name() == "INSTRUMENT") { return true; } } return false; } @@ -92,9 +89,7 @@ class InstrumentationTargetFinder final : public VNVisitor { bool hasMultiple(const std::string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - return it->second.multipleCellps; - } + if (it != instrCfg.end()) { return it->second.multipleCellps; } return false; } // Check if the direct predecessor in the target string has been instrumented, @@ -103,8 +98,7 @@ class InstrumentationTargetFinder final : public VNVisitor { const auto& instrCfg = V3Control::getInstrumentationConfigs(); auto priorTarget = reduce2Depth(split(target), KeyDepth::RelevantModule); auto it = instrCfg.find(priorTarget); - return it != instrCfg.end() - && it->second.processed; + return it != instrCfg.end() && it->second.processed; } bool targetHasFullName(const string& fullname, const string& target) { return fullname == target; @@ -130,50 +124,41 @@ class InstrumentationTargetFinder final : public VNVisitor { return std::vector(iter, end); } // Helper function to reduce a given key to a certain hierarchy level. - enum class KeyDepth { - TopModule = 0, - RelevantModule = 1, - Instance = 2, - FullKey = 3 - }; + enum class KeyDepth { TopModule = 0, RelevantModule = 1, Instance = 2, FullKey = 3 }; string reduce2Depth(std::vector keyTokens, KeyDepth hierarchyLevel) { std::string reducedKey = keyTokens[0]; if (hierarchyLevel == KeyDepth::TopModule) { return keyTokens[0]; } else { int d = static_cast(hierarchyLevel); - for (size_t i = 1; i < keyTokens.size() - d; ++i) { - reducedKey += "." + keyTokens[i]; - } + for (size_t i = 1; i < keyTokens.size() - d; ++i) { reducedKey += "." + keyTokens[i]; } return reducedKey; } } // Helper function for adding the parameters into the tree void addParam(AstModule* modp) { AstVar* paramp = new AstVar(modp->fileline(), VVarType::GPARAM, "INSTRUMENT", - VFlagChildDType{}, nullptr); - paramp->valuep( - new AstConst(modp->fileline(), AstConst::Signed32{}, 0)); - paramp->dtypep(paramp->valuep()->dtypep()); - paramp->ansi(true); - modp->addStmtsp(paramp); + VFlagChildDType{}, nullptr); + paramp->valuep(new AstConst(modp->fileline(), AstConst::Signed32{}, 0)); + paramp->dtypep(paramp->valuep()->dtypep()); + paramp->ansi(true); + modp->addStmtsp(paramp); } // Helper function for adding the parameters into the tree void addPin(AstCell* cellp, bool isInstrumentPath) { int pinnum = 0; if (isInstrumentPath) { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp - = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", - // The pin is set to 1 to enable the instrumentation path - new AstConst(cellp->fileline(), AstConst::Signed32{}, 1)); + AstPin* pinp = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", + // The pin is set to 1 to enable the instrumentation path + new AstConst(cellp->fileline(), AstConst::Signed32{}, 1)); pinp->param(true); cellp->addParamsp(pinp); } else { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp - = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", - new AstParseRef(cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT")); + AstPin* pinp = new AstPin( + cellp->fileline(), pinnum + 1, "INSTRUMENT", + new AstParseRef(cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT")); pinp->param(true); cellp->addParamsp(pinp); } @@ -182,17 +167,13 @@ class InstrumentationTargetFinder final : public VNVisitor { void editInstrData(AstCell* cellp, const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.cellp = cellp; - } + if (it != instrCfg.end()) { it->second.cellp = cellp; } } // Edit the instrumentation data for the pointing module in the map void editInstrData(AstModule* modulep, const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.pointingModulep = modulep; - } + if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } } // Check for multiple cells pointing to the next module void multCellForModp(AstCell* cellp) { @@ -203,8 +184,7 @@ class InstrumentationTargetFinder final : public VNVisitor { m_modp = nullptr; m_cellModp = cellp->modp(); auto modpRepetition = cellModps.count(m_cellModp); - if (modpRepetition > 1 - && !targetHasFullName(m_currHier, m_target)) { + if (modpRepetition > 1 && !targetHasFullName(m_currHier, m_target)) { setMultiple(m_target); } } @@ -212,9 +192,7 @@ class InstrumentationTargetFinder final : public VNVisitor { void setCell(AstCell* cellp, const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.cellp = cellp; - } + if (it != instrCfg.end()) { it->second.cellp = cellp; } } // Insert the original and instrumented module nodes to the map void setInstrModule(AstModule* origModulep, AstModule* instrModulep, const string& target) { @@ -229,34 +207,26 @@ class InstrumentationTargetFinder final : public VNVisitor { void setMultiple(const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.multipleCellps = true; - } + if (it != instrCfg.end()) { it->second.multipleCellps = true; } } // Insert the module node that includes the cell pointing to the targeted module // to the map void setPointingMod(AstModule* modulep, const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.pointingModulep = modulep; - } + if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } } // Set the processed flag void setProcessed(const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.processed = true; - } + if (it != instrCfg.end()) { it->second.processed = true; } } // Insert the top module node of the netlist to the map void setTopMod(AstModule* modulep, const string& target) { auto& instrCfg = V3Control::getInstrumentationConfigs(); auto it = instrCfg.find(target); - if (it != instrCfg.end()) { - it->second.topModulep = modulep; - } + if (it != instrCfg.end()) { it->second.topModulep = modulep; } } // Insert the original and instrumented variable nodes to the map void setVar(AstVar* varp, AstVar* instVarp, const string& target) { @@ -278,49 +248,50 @@ class InstrumentationTargetFinder final : public VNVisitor { //ASTMODULE VISITOR FUNCTION: //Iterates over the existing module nodes in the netlist. - //For the first module in the netlist the node name is checked if it is at the first position in - //the target string provided by the configuration file. If not an error is thown, otherwise the - //modules is checked for an already existing INSTRUMENT parameter. If there is no INSTRUMENT - //parameter present we add it to the module. This parameter is used to control the + //For the first module in the netlist the node name is checked if it is at the first position + //in the target string provided by the configuration file. If not an error is thown, otherwise + //the modules is checked for an already existing INSTRUMENT parameter. If there is no + //INSTRUMENT parameter present we add it to the module. This parameter is used to control the //instrumentation of the target. The module is then added to the map of the instrumentation //configs as the top module. Additionally the hierarchy the function viewed is currently add is - //initialized with the module name. This module hierarchy is used to identify the correct target - //path in the netlist. The function iterates over the children of the module, with the Cells and - //Vars beeing the relevant targets. + //initialized with the module name. This module hierarchy is used to identify the correct + //target path in the netlist. The function iterates over the children of the module, with the + //Cells and Vars beeing the relevant targets. - //After the iteration of the children the m_modp variable needs to be set by the Cell visitor to - //continue or there needs no suitable cell to be found. (See CELL VISITOR FUNCTION & VAR VISITOR - //FUNCTION) Since the module from the m_modp can appear earlier in the tree the fundModp function - //is used to iterate over the netlift from the beginning to find the module. The module node - //displayed by the m_modp variable is then checked if this is the module containing the target - //variable (relevant module) or if it the module containing the cell pointing to the relevant - //module (pointing module). If the module node suits one of these two conditions the module nodes - //are added to the instrumentation configs map. Independetly from these conditions the INSTRUMENT - //parameter is added to the module nodes in the target path. This parameter is used to control - //the instrumentation of the target. - void visit (AstModule* nodep) { + //After the iteration of the children the m_modp variable needs to be set by the Cell visitor + //to continue or there needs no suitable cell to be found. (See CELL VISITOR FUNCTION & VAR + //VISITOR FUNCTION) Since the module from the m_modp can appear earlier in the tree the + //fundModp function is used to iterate over the netlift from the beginning to find the module. + //The module node displayed by the m_modp variable is then checked if this is the module + //containing the target variable (relevant module) or if it the module containing the cell + //pointing to the relevant module (pointing module). If the module node suits one of these two + //conditions the module nodes are added to the instrumentation configs map. Independetly from + //these conditions the INSTRUMENT parameter is added to the module nodes in the target path. + //This parameter is used to control the instrumentation of the target. + void visit(AstModule* nodep) { if (m_initModp) { if (targetHasTop(nodep->name(), m_target)) { m_foundModp = true; m_modp = nodep; m_currHier = nodep->name(); - if (!hasParam(nodep)) { - addParam(nodep); - } + if (!hasParam(nodep)) { addParam(nodep); } if (string::npos == m_target.rfind('.')) { m_targetModp = nodep; - m_foundCellp = true; // Set to true since there is no Instance that the cell visitor could find + m_foundCellp = true; // Set to true since there is no Instance that the cell + // visitor could find } setTopMod(nodep, m_target); iterateChildren(nodep); } else if (!m_foundModp && nodep->name() == "@CONST-POOL@") { - v3error("Verilator-configfile': could not find initial 'module' in 'module.instance.__'" + v3error("Verilator-configfile': could not find initial 'module' in " + "'module.instance.__'" " ... Target: '" << m_target << "'"); m_initModp = false; m_error = true; } - } else if (m_cellModp != nullptr && (nodep = findModp(m_netlist, VN_CAST(m_cellModp, Module))) != nullptr) { + } else if (m_cellModp != nullptr + && (nodep = findModp(m_netlist, VN_CAST(m_cellModp, Module))) != nullptr) { if (targetHasFullName(m_currHier, m_target)) { AstModule* instrModp = nullptr; m_foundModp = true; @@ -329,20 +300,21 @@ class InstrumentationTargetFinder final : public VNVisitor { // Check for prior changes made to the tree if (hasPrior(nodep, m_currHier)) { auto& instrCfg = V3Control::getInstrumentationConfigs(); - instrModp = instrCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule))->second.instrModulep; + instrModp + = instrCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule)) + ->second.instrModulep; editInstrData(instrModp, m_currHier); AstCell* cellp = nullptr; for (AstNode* n = instrModp->op2p(); n; n = n->nextp()) { - if (VN_IS(n, Cell) && (VN_CAST(n, Cell)->modp() == nodep) && instrCfg.find(m_currHier)->second.cellp->name() == n->name()) { + if (VN_IS(n, Cell) && (VN_CAST(n, Cell)->modp() == nodep) + && instrCfg.find(m_currHier)->second.cellp->name() == n->name()) { cellp = VN_CAST(n, Cell); break; } } editInstrData(cellp, m_currHier); } - if (!hasParam(nodep)) { - addParam(nodep); - } + if (!hasParam(nodep)) { addParam(nodep); } instrModp = nodep->cloneTree(false); instrModp->name(nodep->name() + "__inst__" + std::to_string(m_instrIdx)); if (hasMultiple(m_target)) { instrModp->inLibrary(true); } @@ -353,9 +325,7 @@ class InstrumentationTargetFinder final : public VNVisitor { m_foundCellp = false; m_modp = nodep; m_cellModp = nullptr; - if (!hasParam(nodep)) { - addParam(nodep); - } + if (!hasParam(nodep)) { addParam(nodep); } setPointingMod(nodep, m_target); iterateChildren(nodep); } else if (targetHasPrefix(m_currHier, m_target)) { @@ -363,16 +333,14 @@ class InstrumentationTargetFinder final : public VNVisitor { m_foundCellp = false; m_modp = nodep; m_cellModp = nullptr; - if (!hasParam(nodep)) { - addParam(nodep); - } + if (!hasParam(nodep)) { addParam(nodep); } iterateChildren(nodep); } - } else if(!m_error && !m_foundCellp) { - v3error("Verilator-configfile: could not find 'instance' in " + } else if (!m_error && !m_foundCellp) { + v3error("Verilator-configfile: could not find 'instance' in " "'__.instance.__' ... Target string: '" << m_target << "'"); - } else if(!m_error && !m_foundVarp) { + } else if (!m_error && !m_foundVarp) { v3error("Verilator-configfile': could not find '.var' in '__.module.var'" " ... Target: '" << m_target << "'"); @@ -380,32 +348,31 @@ class InstrumentationTargetFinder final : public VNVisitor { } //ASTCELL VISITOR FUNCTION: - //This cell visitor function is called if the module visitor function found a module that matches - //the target string from the config. The first function call should be when visiting the initial - //module in the netlist. When a cell is found that matches the target string and is not marked as - //found, the current hierarchy is updated and the cell marked as found. Additionally, if this is - //the cell in the initial module, the initial module flag is set to false. The in the current - //module existing cells are checked if there are multiple cells linking to the next module in the - //target string. After that the m_modp is updated to match the cell's module pointer, which is - //needed for the next call of the module visitor. Next the pin for the INSTRUMENT parameter is - //added to the cell. This parameter is added either as a constant or as a reference, depending on - //the traversal stage. If there are multiple cells linking to the next module in the target - //string, the multiple flag is set in the instrumentation config map. For the inistial module the - //found cell is then added to the instrumentation configuration map with the current hierarchy as - //the target path. Otherwise the cell is added to the instrumentation configuration map, when the - //current hierarchy with the cell name fully matches a target path, with the last two entrances - //removed (Module, Var). This function ensures that the correct cells in the design hierarchy are - //instrumented and tracked, supporting both unique and repeated module instances. - void visit (AstCell* nodep) { + //This cell visitor function is called if the module visitor function found a module that + //matches the target string from the config. The first function call should be when visiting + //the initial module in the netlist. When a cell is found that matches the target string and is + //not marked as found, the current hierarchy is updated and the cell marked as found. + //Additionally, if this is the cell in the initial module, the initial module flag is set to + //false. The in the current module existing cells are checked if there are multiple cells + //linking to the next module in the target string. After that the m_modp is updated to match + //the cell's module pointer, which is needed for the next call of the module visitor. Next the + //pin for the INSTRUMENT parameter is added to the cell. This parameter is added either as a + //constant or as a reference, depending on the traversal stage. If there are multiple cells + //linking to the next module in the target string, the multiple flag is set in the + //instrumentation config map. For the inistial module the found cell is then added to the + //instrumentation configuration map with the current hierarchy as the target path. Otherwise + //the cell is added to the instrumentation configuration map, when the current hierarchy with + //the cell name fully matches a target path, with the last two entrances removed (Module, Var). + //This function ensures that the correct cells in the design hierarchy are instrumented and + //tracked, supporting both unique and repeated module instances. + void visit(AstCell* nodep) { if (m_initModp) { if (targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; m_foundModp = false; m_initModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { - addPin(nodep, false); - } + if (!hasPin(nodep)) { addPin(nodep, false); } multCellForModp(nodep); setCell(nodep, m_target); } else if (targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { @@ -413,34 +380,30 @@ class InstrumentationTargetFinder final : public VNVisitor { m_foundModp = false; m_initModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { - addPin(nodep, true); - } + if (!hasPin(nodep)) { addPin(nodep, true); } multCellForModp(nodep); setCell(nodep, m_target); } else if (!m_foundCellp && !VN_IS(nodep->nextp(), Cell)) { v3error("Verilator-configfile': could not find initial 'instance' in " - "'topModule.instance.__' ... Target string: '" - << m_target << "'"); + "'topModule.instance.__' ... Target string: '" + << m_target << "'"); m_error = true; m_initModp = false; } - } else if (m_modp != nullptr && targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { + } else if (m_modp != nullptr + && targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; m_foundModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { - addPin(nodep, false); - } + if (!hasPin(nodep)) { addPin(nodep, false); } multCellForModp(nodep); setCell(nodep, m_target); - } else if (m_modp != nullptr && targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { + } else if (m_modp != nullptr + && targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; m_foundModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { - addPin(nodep, false); - } + if (!hasPin(nodep)) { addPin(nodep, false); } multCellForModp(nodep); } } @@ -449,13 +412,13 @@ class InstrumentationTargetFinder final : public VNVisitor { //The var visitor function is used to find the variable that matches the target string from the //config. This is only done if the Cell visitor does not find a matching cell in the current //module of the target hierarchy. Since we therefore know that we will not traverse any further - //in the hierarchy of the model, we can check for this variable. If a variable is found, with its - //name added to the current hierarchy, that siuts the target string, an edited version and the - //original version are added to the instrumentation config map. - void visit (AstVar* nodep) { + //in the hierarchy of the model, we can check for this variable. If a variable is found, with + //its name added to the current hierarchy, that siuts the target string, an edited version and + //the original version are added to the instrumentation config map. + void visit(AstVar* nodep) { if (m_targetModp != nullptr) { - const InstrumentationTarget& target = V3Control::getInstrumentationConfigs().find( - m_currHier)->second; + const InstrumentationTarget& target + = V3Control::getInstrumentationConfigs().find(m_currHier)->second; for (const auto& entry : target.entries) { if (nodep->name() == entry.varTarget) { int width; @@ -463,30 +426,28 @@ class InstrumentationTargetFinder final : public VNVisitor { bool literal = basicp->isLiteralType(); bool implicit = basicp->implicit(); if (!implicit) { - // Since the basicp is not implicit, there should be a rangep indicating the width + // Since the basicp is not implicit, there should be a rangep indicating + // the width width = nodep->basicp()->rangep()->elementsConst(); } bool isUnsupportedType = !literal && !implicit; bool isUnsupportedWidth = literal && width > 64; if (isUnsupportedType || isUnsupportedWidth) { v3error("Verilator-configfile: target variable '" - << nodep->name() << "' in '" - << m_currHier << "' must be a supported type!"); + << nodep->name() << "' in '" << m_currHier + << "' must be a supported type!"); return; } AstVar* varp = nodep->cloneTree(false); varp->name("tmp_" + nodep->name()); varp->origName("tmp_" + nodep->name()); varp->trace(true); - if (varp->varType() == VVarType::WIRE) { - varp->varType(VVarType::VAR); - } + if (varp->varType() == VVarType::WIRE) { varp->varType(VVarType::VAR); } setVar(nodep, varp, m_target); if (string::npos == m_currHier.rfind('.')) { AstModule* modulep = m_modp->cloneTree(false); modulep->name(m_modp->name() + "__inst__" + std::to_string(m_instrIdx)); - setInstrModule( - m_modp, modulep, m_currHier); + setInstrModule(m_modp, modulep, m_currHier); m_initModp = false; } m_foundVarp = true; @@ -524,28 +485,31 @@ public: //################################################################################## // Instrumentation class functions class InstrumentationFunction final : public VNVisitor { - bool m_assignw = false; // Flag if a assignw exists in the netlist - bool m_addedport = false; // Flag if a port was already added - bool m_addedTask = false; // Flag if a task was already added - bool m_addedFunc = false; // Flag if a function was already added - bool m_interface = false; // Flag if the ParseRef node is part of an interface - int m_pinnum = 0; // Pinnumber for the new Port nodes - string m_targetKey; // Stores the target string from the instrumentation config + bool m_assignw = false; // Flag if a assignw exists in the netlist + bool m_addedport = false; // Flag if a port was already added + bool m_addedTask = false; // Flag if a task was already added + bool m_addedFunc = false; // Flag if a function was already added + bool m_interface = false; // Flag if the ParseRef node is part of an interface + int m_pinnum = 0; // Pinnumber for the new Port nodes + string m_targetKey; // Stores the target string from the instrumentation config string m_task_name; - size_t m_targetIndex = 0; // Index of the target variable in the instrumentation config - AstAlways* m_alwaysp = nullptr; // Stores the added always node - AstAssignW* m_assignwp = nullptr; // Stores the added assignw node - AstGenBlock* m_instGenBlock = nullptr; // Store the GenBlock node for instrumentation hierarchy check - AstTask* m_taskp = nullptr; // // Stores the created task node - AstFunc* m_funcp = nullptr; // Stores the created function node - AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node - AstTaskRef* m_taskrefp = nullptr; // Stores the created taskref node - AstModule* m_current_module = nullptr; // Stores the currenty visited module - AstModule* m_current_module_cell_check = nullptr; // Stores the module node(used by cell visitor) - AstVar* m_tmp_varp = nullptr; // Stores the instrumented variable node - AstVar* m_orig_varp = nullptr; // Stores the original variable node - AstVar* m_orig_varp_instMod = nullptr; // Stores the original variable node in instrumented module node - AstPort* m_orig_portp = nullptr; // Stores the original port node + size_t m_targetIndex = 0; // Index of the target variable in the instrumentation config + AstAlways* m_alwaysp = nullptr; // Stores the added always node + AstAssignW* m_assignwp = nullptr; // Stores the added assignw node + AstGenBlock* m_instGenBlock + = nullptr; // Store the GenBlock node for instrumentation hierarchy check + AstTask* m_taskp = nullptr; // // Stores the created task node + AstFunc* m_funcp = nullptr; // Stores the created function node + AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node + AstTaskRef* m_taskrefp = nullptr; // Stores the created taskref node + AstModule* m_current_module = nullptr; // Stores the currenty visited module + AstModule* m_current_module_cell_check + = nullptr; // Stores the module node(used by cell visitor) + AstVar* m_tmp_varp = nullptr; // Stores the instrumented variable node + AstVar* m_orig_varp = nullptr; // Stores the original variable node + AstVar* m_orig_varp_instMod + = nullptr; // Stores the original variable node in instrumented module node + AstPort* m_orig_portp = nullptr; // Stores the original port node // METHODS //---------------------------------------------------------------------------------- @@ -561,43 +525,33 @@ class InstrumentationFunction final : public VNVisitor { } // Get the Cell nodep pointer from the configuration map for the given key AstCell* getMapEntryCell(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { - return cfg->cellp; - } + if (auto cfg = getInstrCfg(key)) { return cfg->cellp; } return nullptr; } // Get the instrumented Module node pointer from the configuration map for the given key AstModule* getMapEntryInstModule(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { - return cfg->instrModulep; - } + if (auto cfg = getInstrCfg(key)) { return cfg->instrModulep; } return nullptr; } // Get the Module node pointer pointing to the instrumented/original module from the // configuration map for the given key AstModule* getMapEntryPointingModule(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { - return cfg->pointingModulep; - } + if (auto cfg = getInstrCfg(key)) { return cfg->pointingModulep; } return nullptr; } // Get the instrumented variable node pointer from the configuration map for the given key AstVar* getMapEntryInstVar(const std::string& key, size_t index) { if (auto cfg = getInstrCfg(key)) { const auto& entries = cfg->entries; - if (index < entries.size()) { - return entries[index].instrVarps; - } + if (index < entries.size()) { return entries[index].instrVarps; } } return nullptr; } // Get the original variable node pointer from the configuration map for the given key - AstVar* getMapEntryVar(const std::string& key, size_t index) { + AstVar* getMapEntryVar(const std::string& key, size_t index) { if (auto cfg = getInstrCfg(key)) { const auto& entries = cfg->entries; - if (index < entries.size()) { - return entries[index].origVarps; - } + if (index < entries.size()) { return entries[index].origVarps; } } return nullptr; } @@ -606,8 +560,7 @@ class InstrumentationFunction final : public VNVisitor { bool isInstModEntry(AstModule* nodep, const std::string& key) { const auto& map = V3Control::getInstrumentationConfigs(); const auto instrCfg = map.find(key); - if (instrCfg != map.end() - && instrCfg->second.instrModulep == nodep) { + if (instrCfg != map.end() && instrCfg->second.instrModulep == nodep) { return true; } else { return false; @@ -653,10 +606,8 @@ class InstrumentationFunction final : public VNVisitor { const auto instrCfg = map.find(key); if (instrCfg != map.end()) { const auto& entries = instrCfg->second.entries; - if (index < entries.size()) { - return entries[index].instrID; - } - return -1; // Return -1 if index is out of bounds + if (index < entries.size()) { return entries[index].instrID; } + return -1; // Return -1 if index is out of bounds } else { return -1; } @@ -667,9 +618,7 @@ class InstrumentationFunction final : public VNVisitor { const auto instrCfg = map.find(key); if (instrCfg != map.end()) { const auto& entries = instrCfg->second.entries; - if (index < entries.size()) { - return entries[index].instrFunc; - } + if (index < entries.size()) { return entries[index].instrFunc; } return ""; } else { return ""; @@ -687,7 +636,8 @@ class InstrumentationFunction final : public VNVisitor { if (orig_varp->basicp()->isLiteralType() || orig_varp->basicp()->implicit()) { int width; if (orig_varp->basicp()->implicit()) { - // Since Var is implicit set/assume the width as 1 like in V3Width.cpp in the AstVar visitor + // Since Var is implicit set/assume the width as 1 like in V3Width.cpp in the + // AstVar visitor width = 1; } else { width = orig_varp->basicp()->rangep()->elementsConst(); @@ -732,19 +682,21 @@ class InstrumentationFunction final : public VNVisitor { // - If the module is an instrumented module entry and has not been done, it creates a new //task for the instrumentation function, adds the temporary variable, and creates a task //reference to the instrumentation function. - // - If the module is a pointing module or a top module and has no multiple cellps, it checks + // - If the module is a pointing module or a top module and has no multiple cellps, it + // checks //the cell for the target key and counts the pins. This pin count is used in the CELL VISITOR //FUNCTION to set a siutable pin number for the INSTRUMENT parameter. Look there fore further //information. - // - If the module is a pointing module and has multiple cellps, it creates a begin block with + // - If the module is a pointing module and has multiple cellps, it creates a begin block + // with //a conditional statement to select between the instrumented and original cell. // Additionally like in the previous case, the pin count is used to set a suitable pin - //number for the INSTRUMENT parameter.\ Since the cell which need to be edited are located not in - //the original module, but in the pointing/top module, the current_module_cell_check variable is - //set to the module visited by the function and fulfilling this condition. + //number for the INSTRUMENT parameter.\ Since the cell which need to be edited are located not + //in the original module, but in the pointing/top module, the current_module_cell_check + //variable is set to the module visited by the function and fulfilling this condition. void visit(AstModule* nodep) { - const InstrumentationTarget& target = V3Control::getInstrumentationConfigs().find( - m_targetKey)->second; + const InstrumentationTarget& target + = V3Control::getInstrumentationConfigs().find(m_targetKey)->second; const auto& entries = target.entries; for (m_targetIndex = 0; m_targetIndex < entries.size(); ++m_targetIndex) { const auto& entry = entries[m_targetIndex]; @@ -767,7 +719,7 @@ class InstrumentationFunction final : public VNVisitor { } } if (!m_addedTask && !m_addedFunc) { - auto m_dpip = createDPIInterface(nodep ,m_orig_varp, m_task_name); + auto m_dpip = createDPIInterface(nodep, m_orig_varp, m_task_name); if (VN_IS(m_dpip, Func)) { m_funcp = VN_CAST(m_dpip, Func); m_funcp->dpiImport(true); @@ -790,26 +742,27 @@ class InstrumentationFunction final : public VNVisitor { if (m_taskp != nullptr) { m_taskrefp = new AstTaskRef{ - nodep->fileline(), m_task_name, - new AstArg{nodep->fileline(), m_tmp_varp->name(), - new AstVarRef{nodep->fileline(), m_tmp_varp, VAccess::WRITE}}}; + nodep->fileline(), m_task_name, + new AstArg{nodep->fileline(), m_tmp_varp->name(), + new AstVarRef{nodep->fileline(), m_tmp_varp, VAccess::WRITE}}}; m_taskrefp->taskp(m_taskp); - m_alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, nullptr, nullptr}; + m_alwaysp + = new AstAlways{nodep->fileline(), VAlwaysKwd::ALWAYS, nullptr, nullptr}; nodep->addStmtsp(m_alwaysp); } if (m_funcp != nullptr) { m_funcrefp = new AstFuncRef{nodep->fileline(), m_funcp, nullptr}; - m_assignwp = new AstAssignW{nodep->fileline(), - new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_tmp_varp->name()}, - m_funcrefp}; + m_assignwp + = new AstAssignW{nodep->fileline(), + new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, + m_tmp_varp->name()}, + m_funcrefp}; nodep->addStmtsp(m_assignwp); } if (m_targetIndex == entries.size() - 1) { setDone(nodep); } for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { - if (VN_IS(n, Port)) { - m_pinnum = VN_CAST(n, Port)->pinNum(); - } + if (VN_IS(n, Port)) { m_pinnum = VN_CAST(n, Port)->pinNum(); } } iterateChildren(nodep); } else if ((std::count(m_targetKey.begin(), m_targetKey.end(), '.') > 0) @@ -829,8 +782,8 @@ class InstrumentationFunction final : public VNVisitor { nodep->fileline(), new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT"}, m_instGenBlock, - new AstGenBlock{nodep->fileline(), "", getMapEntryCell(m_targetKey)->cloneTree(false), - false}}; + new AstGenBlock{nodep->fileline(), "", + getMapEntryCell(m_targetKey)->cloneTree(false), false}}; nodep->addStmtsp(genifp); iterateChildren(m_instGenBlock); @@ -856,9 +809,9 @@ class InstrumentationFunction final : public VNVisitor { //If no port is added yet, two new ports are added to the current module. //This enabled the instrumentation of the ouput port and link this instrumented port to the //modules reading from the original port. The idea behind this function is to set the - //instrumented port on the position of the original port in the module and move the original port - //to another pin number. - //This should ensure the linking over the name and the port position in the module should work. + //instrumented port on the position of the original port in the module and move the original + //port to another pin number. This should ensure the linking over the name and the port + //position in the module should work. void visit(AstPort* nodep) { if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::OUTPUT && nodep->name() == m_orig_varp->name() && !m_addedport) { @@ -876,15 +829,16 @@ class InstrumentationFunction final : public VNVisitor { //ASTCELL VISITOR FUNCTION: //This function visits the cell nodes in the module pointing to the instrumented module. //Depending if hasMultiple is set for the target key, two different actions are performed: - // - If hasMultiple is false, the cell is modified to link to the instrumented module and the - //children are iterated. This ensures that the instrumented mopdule is used in the cell. Also if - //the original variable is an output variable, the children of this cell nodes are visited by the - //ASTPIN VISITOR FUNCTION. + // - If hasMultiple is false, the cell is modified to link to the instrumented module and + // the + //children are iterated. This ensures that the instrumented mopdule is used in the cell. Also + //if the original variable is an output variable, the children of this cell nodes are visited + //by the ASTPIN VISITOR FUNCTION. // - If hasMultiple is true, the cell is unlinked from the back and deleted. // This ensures that the cell is not used anymore in the module, and the conditional //statment deciding between the instrumented and the original cell can be created/used. A third - //action is performed if the variable beeing instrumented is an ouput variable. In this case the - //children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. + //action is performed if the variable beeing instrumented is an ouput variable. In this case + //the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. void visit(AstCell* nodep) { if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) && nodep == getMapEntryCell(m_targetKey)) { @@ -907,7 +861,8 @@ class InstrumentationFunction final : public VNVisitor { //variable name. This is done to ensure that the pin is correctly linked to the instrumented //variable in the cell. void visit(AstPin* nodep) { - if (nodep->name() == m_orig_varp->name() && m_orig_varp->direction() == VDirection::INPUT) { + if (nodep->name() == m_orig_varp->name() + && m_orig_varp->direction() == VDirection::INPUT) { iterateChildren(nodep); } else if (nodep->name() == m_orig_varp->name()) { nodep->name(m_tmp_varp->name()); @@ -923,8 +878,8 @@ class InstrumentationFunction final : public VNVisitor { AstVar* tmp_var_task = nullptr; instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, - new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, - VSigning::SIGNED, 32, 0}}; + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; instrID->direction(VDirection::INPUT); var_x_task = m_orig_varp->cloneTree(false); @@ -949,8 +904,8 @@ class InstrumentationFunction final : public VNVisitor { AstVar* var_x_func = nullptr; instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, - new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, - VSigning::SIGNED, 32, 0}}; + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; instrID->direction(VDirection::INPUT); var_x_func = m_orig_varp->cloneTree(false); @@ -963,14 +918,13 @@ class InstrumentationFunction final : public VNVisitor { } //ASTALWAYS VISITOR FUNCTION: - //The function is used to add the task reference node to the always node and further specify the - //always node. + //The function is used to add the task reference node to the always node and further specify + //the always node. void visit(AstAlways* nodep) { if (nodep == m_alwaysp && m_current_module != nullptr) { AstBegin* newBegin = nullptr; - m_taskrefp - = new AstTaskRef{nodep->fileline(), m_task_name, nullptr}; + m_taskrefp = new AstTaskRef{nodep->fileline(), m_task_name, nullptr}; newBegin = new AstBegin{nodep->fileline(), "", new AstStmtExpr{nodep->fileline(), m_taskrefp}, false}; @@ -994,7 +948,8 @@ class InstrumentationFunction final : public VNVisitor { constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, getMapEntryFaultCase(m_targetKey, m_targetIndex)}; - AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + AstVarRef* added_varrefp + = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); @@ -1006,7 +961,8 @@ class InstrumentationFunction final : public VNVisitor { } //ASTFUNCREF VISITOR FUNCTION: - //The function is used to further specify the function reference node called by the assignw node + //The function is used to further specify the function reference node called by the assignw + //node void visit(AstFuncRef* nodep) { if (nodep == m_funcrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; @@ -1014,7 +970,8 @@ class InstrumentationFunction final : public VNVisitor { constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, getMapEntryFaultCase(m_targetKey, m_targetIndex)}; - AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + AstVarRef* added_varrefp + = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); @@ -1028,35 +985,31 @@ class InstrumentationFunction final : public VNVisitor { //assignment. void visit(AstAssignW* nodep) { if (m_current_module != nullptr) { - if (nodep != m_assignwp) { - m_assignw = true; - } + if (nodep != m_assignwp) { m_assignw = true; } iterateChildren(nodep); } m_assignw = false; m_interface = false; } - // These two function are used to circumvent the instrumentation of ParseRef nodes for interfaces + // These two function are used to circumvent the instrumentation of ParseRef nodes for + // interfaces void visit(AstDot* nodep) { - if (m_current_module != nullptr) { - m_interface = true; - } + if (m_current_module != nullptr) { m_interface = true; } } void visit(AstReplicate* nodep) { - if (m_current_module != nullptr) { - m_interface = true; - } + if (m_current_module != nullptr) { m_interface = true; } } //ASTPARSE REF VISITOR FUNCTION: - //The function is used to change the parseref nodes to link to the instrumented variable instead - //of the original variable. Depending on the direction of the original variable, different - //actions are performed: + //The function is used to change the parseref nodes to link to the instrumented variable + //instead of the original variable. Depending on the direction of the original variable, + //different actions are performed: // - If the original variable is not an output variable and the assignment is true, the //parseref node is changed to link to the instrumented variable. This ensures that the //instrumented variable is used in the assignment. - // - If the original variable is an input variable, every parseref node is changed to link to + // - If the original variable is an input variable, every parseref node is changed to link + // to //the instrumented variable. This ensures that the instrumented variable is used as the new //input. void visit(AstParseRef* nodep) { diff --git a/src/Verilator.cpp b/src/Verilator.cpp index de0b3f2a1..75704a70b 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -154,9 +154,7 @@ static void process() { v3Global.vlExit(0); } - if (v3Global.opt.dumpSignals()) { - V3DumpSignals::dumpSignals(v3Global.rootp()); - } + if (v3Global.opt.dumpSignals()) { V3DumpSignals::dumpSignals(v3Global.rootp()); } // Instrument Design with the configurations given in .vlt file if (v3Global.opt.instrument()) { diff --git a/test_regress/t/t_instrumentationDPI.cpp b/test_regress/t/t_instrumentationDPI.cpp index d74027ab9..78eb0a723 100644 --- a/test_regress/t/t_instrumentationDPI.cpp +++ b/test_regress/t/t_instrumentationDPI.cpp @@ -11,18 +11,15 @@ #include #include -extern "C" short instrument_var(int id, const svLogic *x) { - switch (id) - { - case 0: - return 0; +extern "C" short instrument_var(int id, const svLogic* x) { + switch (id) { + case 0: return 0; case 1: // Stuck at 1 Fault Injection return 1; case 2: // Inverter/Bit flip Fault injection (provisional) return !x; - default: - return *x; + default: return *x; } } From 5624b707e161e5ec2cf164b3e0947fd8cad589f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 1 Oct 2025 13:47:43 +0000 Subject: [PATCH 05/20] Apply 'make format' --- src/CMakeLists.txt | 4 +- src/Makefile_obj.in | 2 +- src/V3Control.cpp | 27 ++-- src/V3Control.h | 14 +- ...V3Instrumentation.cpp => V3Instrument.cpp} | 130 +++++++++++------- src/{V3Instrumentation.h => V3Instrument.h} | 6 +- src/Verilator.cpp | 7 +- src/verilog.y | 2 +- ...t_instrumentation.cpp => t_instrument.cpp} | 2 +- ...t_instrumentation.out => t_instrument.out} | 0 .../{t_instrumentation.py => t_instrument.py} | 6 +- .../t/{t_instrumentation.v => t_instrument.v} | 0 ...t_instrumentation.vlt => t_instrument.vlt} | 0 ...rumentationDPI.cpp => t_instrumentDPI.cpp} | 0 test_regress/t/t_instrument_bad.out | 1 + test_regress/t/t_instrument_bad.py | 18 +++ test_regress/t/t_instrument_bad.vlt | 17 +++ 17 files changed, 150 insertions(+), 86 deletions(-) rename src/{V3Instrumentation.cpp => V3Instrument.cpp} (92%) rename src/{V3Instrumentation.h => V3Instrument.h} (89%) rename test_regress/t/{t_instrumentation.cpp => t_instrument.cpp} (93%) rename test_regress/t/{t_instrumentation.out => t_instrument.out} (100%) rename test_regress/t/{t_instrumentation.py => t_instrument.py} (83%) rename test_regress/t/{t_instrumentation.v => t_instrument.v} (100%) rename test_regress/t/{t_instrumentation.vlt => t_instrument.vlt} (100%) rename test_regress/t/{t_instrumentationDPI.cpp => t_instrumentDPI.cpp} (100%) create mode 100644 test_regress/t/t_instrument_bad.out create mode 100755 test_regress/t/t_instrument_bad.py create mode 100644 test_regress/t/t_instrument_bad.vlt diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index acc2eaf18..5db093c05 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,7 +117,7 @@ set(HEADERS V3Inline.h V3Inst.h V3InstrCount.h - V3Instrumentation.h + V3Instrument.h V3Interface.h V3LangCode.h V3LanguageWords.h @@ -290,7 +290,7 @@ set(COMMON_SOURCES V3Inline.cpp V3Inst.cpp V3InstrCount.cpp - V3Instrumentation.cpp + V3Instrument.cpp V3Interface.cpp V3Life.cpp V3LifePost.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 939aee11b..5f6704a3b 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -287,7 +287,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Inline.o \ V3Inst.o \ V3InstrCount.o \ - V3Instrumentation.o \ + V3Instrument.o \ V3Interface.o \ V3Life.o \ V3LifePost.o \ diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 605d9dbef..e0fd078db 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -576,7 +576,7 @@ class V3ControlResolver final { uint8_t m_mode = NONE; std::unordered_map m_hierWorkers; FileLine* m_profileFileLine = nullptr; - std::map m_instrCfg; + std::map m_instrCfg; V3ControlResolver() = default; ~V3ControlResolver() = default; @@ -652,8 +652,8 @@ public: return {prefix, varTarget}; } // Add the instrumentation config data to the map to create the initial map (Used in verilog.y) - void addInstrumentationConfigs(FileLine* fl, const string& instrFunction, int instrID, - const string& target) { + void addInstrumentCfg(FileLine* fl, const string& instrFunction, int instrID, + const string& target) { // Error MSG if the instrumentation of the top module is not possible if ((std::count(target.begin(), target.end(), '.') < 2)) { v3fatal( @@ -664,19 +664,21 @@ public: // Implement custom iterator to remove the last part of the target and insert it into the // vector of the map If the target string is the same as one already in the map, push the // var to the vector - auto [prefix, varTarget] = splitPrefixAndVar(target); - InstrumentationEntry entry{instrID, instrFunction, varTarget}; + auto result = splitPrefixAndVar(target); + auto prefix = result.first; + auto varTarget = result.second; + InstrumentEntry entry{instrID, instrFunction, varTarget, {}, {}}; auto it = m_instrCfg.find(prefix); if (it != m_instrCfg.end()) { it->second.entries.push_back(entry); } else { // Create a new entry in the map - InstrumentationTarget newTarget; + InstrumentTarget newTarget; newTarget.entries.push_back(entry); m_instrCfg[prefix] = std::move(newTarget); } } - std::map& getInstrumentationConfigs() { + std::map& getInstrumentCfg() { return m_instrCfg; } }; @@ -737,9 +739,9 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) { V3ControlResolver::s().modules().at(module).addModulePragma(pragma); } -void V3Control::addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, - int instrID, const string& target) { - V3ControlResolver::s().addInstrumentationConfigs(fl, instrumentationfunc, instrID, target); +void V3Control::addInstrumentCfg(FileLine* fl, const string& instrumentfunc, int instrID, + const string& target) { + V3ControlResolver::s().addInstrumentCfg(fl, instrumentfunc, instrID, target); } void V3Control::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { @@ -903,9 +905,8 @@ int V3Control::getHierWorkers(const string& model) { FileLine* V3Control::getHierWorkersFileLine(const string& model) { return V3ControlResolver::s().getHierWorkersFileLine(model); } -std::map& -V3Control::getInstrumentationConfigs() { - return V3ControlResolver::s().getInstrumentationConfigs(); +std::map& V3Control::getInstrumentCfg() { + return V3ControlResolver::s().getInstrumentCfg(); } uint64_t V3Control::getProfileData(const string& hierDpi) { return V3ControlResolver::s().getProfileData(hierDpi); diff --git a/src/V3Control.h b/src/V3Control.h index 7528ac3ae..e57d764e9 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -33,15 +33,16 @@ struct LengthThenLexiographic final { return a < b; } }; -struct InstrumentationEntry final { +struct InstrumentEntry final { int instrID; std::string instrFunc; std::string varTarget; AstVar* origVarps; AstVar* instrVarps; + bool found = false; }; -struct InstrumentationTarget final { - std::vector entries; +struct InstrumentTarget final { + std::vector entries; AstModule* origModulep; AstModule* instrModulep; AstModule* topModulep; @@ -69,10 +70,9 @@ public: static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents, const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); - static void addInstrumentationConfigs(FileLine* fl, const string& instrumentationfunc, - int instrID, const string& target); - static std::map& - getInstrumentationConfigs(); + static void addInstrumentCfg(FileLine* fl, const string& instrumentfunc, int instrID, + const string& target); + static std::map& getInstrumentCfg(); static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, diff --git a/src/V3Instrumentation.cpp b/src/V3Instrument.cpp similarity index 92% rename from src/V3Instrumentation.cpp rename to src/V3Instrument.cpp index 17c9ba047..ee3f02c65 100644 --- a/src/V3Instrumentation.cpp +++ b/src/V3Instrument.cpp @@ -24,7 +24,7 @@ #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT -#include "V3Instrumentation.h" +#include "V3Instrument.h" #include "V3Control.h" #include "V3File.h" @@ -40,7 +40,7 @@ VL_DEFINE_DEBUG_FUNCTIONS; //################################################################################## // Instrumentation class finder -class InstrumentationTargetFinder final : public VNVisitor { +class InstrumentTargetFndr final : public VNVisitor { AstNetlist* m_netlist = nullptr; // Enable traversing from the beginning if the visitor is to deep AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node @@ -87,7 +87,7 @@ class InstrumentationTargetFinder final : public VNVisitor { } // Check if the multipleCellps flag is set for the given target bool hasMultiple(const std::string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { return it->second.multipleCellps; } return false; @@ -95,7 +95,7 @@ class InstrumentationTargetFinder final : public VNVisitor { // Check if the direct predecessor in the target string has been instrumented, // to create the correct link between the already instrumented module and the current one. bool hasPrior(AstModule* modulep, const string& target) { - const auto& instrCfg = V3Control::getInstrumentationConfigs(); + const auto& instrCfg = V3Control::getInstrumentCfg(); auto priorTarget = reduce2Depth(split(target), KeyDepth::RelevantModule); auto it = instrCfg.find(priorTarget); return it != instrCfg.end() && it->second.processed; @@ -165,13 +165,13 @@ class InstrumentationTargetFinder final : public VNVisitor { } // Edit the instrumentation data for the cell in the map void editInstrData(AstCell* cellp, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.cellp = cellp; } } // Edit the instrumentation data for the pointing module in the map void editInstrData(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } } @@ -190,13 +190,13 @@ class InstrumentationTargetFinder final : public VNVisitor { } // Insert the cell node that is/will pointing/point to the targeted module void setCell(AstCell* cellp, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.cellp = cellp; } } // Insert the original and instrumented module nodes to the map void setInstrModule(AstModule* origModulep, AstModule* instrModulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.origModulep = origModulep; @@ -205,38 +205,39 @@ class InstrumentationTargetFinder final : public VNVisitor { } // Set the multipleCellps flag void setMultiple(const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.multipleCellps = true; } } // Insert the module node that includes the cell pointing to the targeted module // to the map void setPointingMod(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } } // Set the processed flag void setProcessed(const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.processed = true; } } // Insert the top module node of the netlist to the map void setTopMod(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { it->second.topModulep = modulep; } } // Insert the original and instrumented variable nodes to the map void setVar(AstVar* varp, AstVar* instVarp, const string& target) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); auto it = instrCfg.find(target); if (it != instrCfg.end()) { for (auto& entry : it->second.entries) { if (entry.varTarget == varp->name()) { entry.origVarps = varp; entry.instrVarps = instVarp; + entry.found = true; return; } } @@ -299,7 +300,7 @@ class InstrumentationTargetFinder final : public VNVisitor { m_cellModp = nullptr; // Check for prior changes made to the tree if (hasPrior(nodep, m_currHier)) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); instrModp = instrCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule)) ->second.instrModulep; @@ -417,8 +418,8 @@ class InstrumentationTargetFinder final : public VNVisitor { //the original version are added to the instrumentation config map. void visit(AstVar* nodep) { if (m_targetModp != nullptr) { - const InstrumentationTarget& target - = V3Control::getInstrumentationConfigs().find(m_currHier)->second; + const InstrumentTarget& target + = V3Control::getInstrumentCfg().find(m_currHier)->second; for (const auto& entry : target.entries) { if (nodep->name() == entry.varTarget) { int width; @@ -451,6 +452,11 @@ class InstrumentationTargetFinder final : public VNVisitor { m_initModp = false; } m_foundVarp = true; + } else if (nodep->nextp() == nullptr && !entry.found) { + v3error("Verilator-configfile': could not find defined 'var' in " + "'topModule.instance.var' ... Target string: '" + << m_target+"."+entry.varTarget << "'"); + return; } } } @@ -461,8 +467,8 @@ class InstrumentationTargetFinder final : public VNVisitor { public: // CONSTRUCTOR //------------------------------------------------------------------------------- - explicit InstrumentationTargetFinder(AstNetlist* nodep) { - const auto& instrCfg = V3Control::getInstrumentationConfigs(); + explicit InstrumentTargetFndr(AstNetlist* nodep) { + const auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { m_netlist = nodep; m_target = pair.first; @@ -479,12 +485,12 @@ public: m_instrIdx++; } }; - ~InstrumentationTargetFinder() override = default; + ~InstrumentTargetFndr() override = default; }; //################################################################################## // Instrumentation class functions -class InstrumentationFunction final : public VNVisitor { +class InstrumentFunc final : public VNVisitor { bool m_assignw = false; // Flag if a assignw exists in the netlist bool m_addedport = false; // Flag if a port was already added bool m_addedTask = false; // Flag if a task was already added @@ -514,8 +520,8 @@ class InstrumentationFunction final : public VNVisitor { // METHODS //---------------------------------------------------------------------------------- // Find the relevant instrumentation config in the map corresponding to the given key - const InstrumentationTarget* getInstrCfg(const std::string& key) { - const auto& map = V3Control::getInstrumentationConfigs(); + const InstrumentTarget* getInstrCfg(const std::string& key) { + const auto& map = V3Control::getInstrumentCfg(); auto instrCfg = map.find(key); if (instrCfg != map.end()) { return &instrCfg->second; @@ -558,7 +564,7 @@ class InstrumentationFunction final : public VNVisitor { // Check if the given module node pointer is an instrumented module entry in the configuration // map for the given key bool isInstModEntry(AstModule* nodep, const std::string& key) { - const auto& map = V3Control::getInstrumentationConfigs(); + const auto& map = V3Control::getInstrumentCfg(); const auto instrCfg = map.find(key); if (instrCfg != map.end() && instrCfg->second.instrModulep == nodep) { return true; @@ -568,7 +574,7 @@ class InstrumentationFunction final : public VNVisitor { } // Check if the given module node pointer is the top module entry in the configuration map bool isTopModEntry(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { if (nodep == pair.second.topModulep) { return true; } } @@ -576,7 +582,7 @@ class InstrumentationFunction final : public VNVisitor { } // Check if the given module node pointer is the pointing module entry in the configuration map bool isPointingModEntry(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { if (nodep == pair.second.pointingModulep) { return true; } } @@ -584,7 +590,7 @@ class InstrumentationFunction final : public VNVisitor { } // Check if the given module node pointer has already been instrumented/done flag has been set bool isDone(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { if (nodep == pair.second.instrModulep) { return pair.second.done; } } @@ -592,7 +598,7 @@ class InstrumentationFunction final : public VNVisitor { } // Check if the multipleCellps flag is set for the given key in the configuration map bool hasMultiple(const std::string& key) { - const auto& map = V3Control::getInstrumentationConfigs(); + const auto& map = V3Control::getInstrumentCfg(); const auto instrCfg = map.find(key); if (instrCfg != map.end()) { return instrCfg->second.multipleCellps; @@ -600,9 +606,25 @@ class InstrumentationFunction final : public VNVisitor { return false; } } + // Check if the module and instances defined in the target string were found in + // the previous step + bool hasNullptr(const std::pair &pair) { + bool moduleNullptr = pair.second.origModulep == nullptr; + bool cellNullptr = pair.second.cellp == nullptr; + return moduleNullptr || cellNullptr; + } + // Check if the, in the target string, defined variable was found in the previous step + bool isFound(const std::pair &pair) { + for (auto& entry : pair.second.entries) { + if (entry.found == false) { + return entry.found; + } + } + return true; + } // Get the fault case for the given key in the configuration map int getMapEntryFaultCase(const std::string& key, size_t index) { - const auto& map = V3Control::getInstrumentationConfigs(); + const auto& map = V3Control::getInstrumentCfg(); const auto instrCfg = map.find(key); if (instrCfg != map.end()) { const auto& entries = instrCfg->second.entries; @@ -614,7 +636,7 @@ class InstrumentationFunction final : public VNVisitor { } // Get the instrumentation function name for the given key in the configuration map string getMapEntryFunction(const std::string& key, size_t index) { - const auto& map = V3Control::getInstrumentationConfigs(); + const auto& map = V3Control::getInstrumentCfg(); const auto instrCfg = map.find(key); if (instrCfg != map.end()) { const auto& entries = instrCfg->second.entries; @@ -626,7 +648,7 @@ class InstrumentationFunction final : public VNVisitor { } // Set the done flag for the given module node pointer in the configuraiton map void setDone(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentationConfigs(); + auto& instrCfg = V3Control::getInstrumentCfg(); for (auto& pair : instrCfg) { if (nodep == pair.second.instrModulep) { pair.second.done = true; } } @@ -664,13 +686,19 @@ class InstrumentationFunction final : public VNVisitor { //ASTNETLIST VISITOR FUNCTION: //Loop over map entries for module nodes and add them to the tree - void visit(AstNetlist* nodep) { - const auto& instrCfg = V3Control::getInstrumentationConfigs(); + void visit(AstNetlist* nodep) override { + const auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { - nodep->addModulesp(pair.second.instrModulep); - m_targetKey = pair.first; - iterateChildren(nodep); - m_assignw = false; + if (hasNullptr(pair) || !isFound(pair)) { + v3error("Verilator-configfile: Incomplete instrumentation configuration for target '" + << pair.first<< "'. Please check previous Errors from V3Instrument:findTargets and ensure" + << " all necessary components are correct defined."); + } else { + nodep->addModulesp(pair.second.instrModulep); + m_targetKey = pair.first; + iterateChildren(nodep); + m_assignw = false; + } } } @@ -694,12 +722,11 @@ class InstrumentationFunction final : public VNVisitor { //number for the INSTRUMENT parameter.\ Since the cell which need to be edited are located not //in the original module, but in the pointing/top module, the current_module_cell_check //variable is set to the module visited by the function and fulfilling this condition. - void visit(AstModule* nodep) { - const InstrumentationTarget& target - = V3Control::getInstrumentationConfigs().find(m_targetKey)->second; + void visit(AstModule* nodep) override { + const auto& instrCfg = V3Control::getInstrumentCfg().find(m_targetKey); + const InstrumentTarget& target = instrCfg->second; const auto& entries = target.entries; for (m_targetIndex = 0; m_targetIndex < entries.size(); ++m_targetIndex) { - const auto& entry = entries[m_targetIndex]; m_tmp_varp = getMapEntryInstVar(m_targetKey, m_targetIndex); m_orig_varp = getMapEntryVar(m_targetKey, m_targetIndex); m_task_name = getMapEntryFunction(m_targetKey, m_targetIndex); @@ -944,9 +971,9 @@ class InstrumentationFunction final : public VNVisitor { void visit(AstTaskRef* nodep) { if (nodep == m_taskrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; - - constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, - getMapEntryFaultCase(m_targetKey, m_targetIndex)}; + constp_id = new AstConst{ + nodep->fileline(), AstConst::Unsized32{}, + static_cast(getMapEntryFaultCase(m_targetKey, m_targetIndex))}; AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; @@ -967,8 +994,9 @@ class InstrumentationFunction final : public VNVisitor { if (nodep == m_funcrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; - constp_id = new AstConst{nodep->fileline(), AstConst::Unsized32{}, - getMapEntryFaultCase(m_targetKey, m_targetIndex)}; + constp_id = new AstConst{ + nodep->fileline(), AstConst::Unsized32{}, + static_cast(getMapEntryFaultCase(m_targetKey, m_targetIndex))}; AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; @@ -1028,8 +1056,8 @@ class InstrumentationFunction final : public VNVisitor { public: // CONSTRUCTORS - explicit InstrumentationFunction(AstNetlist* nodep) { iterate(nodep); } - ~InstrumentationFunction() override = default; + explicit InstrumentFunc(AstNetlist* nodep) { iterate(nodep); } + ~InstrumentFunc() override = default; }; //################################################################################## @@ -1037,15 +1065,15 @@ public: // Function to find instrumentation targets and additional information for the instrumentation // process -void V3Instrumentation::findTargets(AstNetlist* nodep) { +void V3Instrument::findTargets(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstrumentationTargetFinder{nodep}; } + { InstrumentTargetFndr{nodep}; } V3Global::dumpCheckGlobalTree("instrumentationFinder", 0, dumpTreeEitherLevel() >= 3); } // Function for the actual instrumentation process -void V3Instrumentation::instrument(AstNetlist* nodep) { +void V3Instrument::instrument(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstrumentationFunction{nodep}; } + { InstrumentFunc{nodep}; } V3Global::dumpCheckGlobalTree("instrumentationFunction", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3Instrumentation.h b/src/V3Instrument.h similarity index 89% rename from src/V3Instrumentation.h rename to src/V3Instrument.h index 148672203..34ef78c62 100644 --- a/src/V3Instrumentation.h +++ b/src/V3Instrument.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef VERILATOR_V3INSTRUMENTATION_H_ -#define VERILATOR_V3INSTRUMENTATION_H_ +#ifndef VERILATOR_V3INSTRUMENT_H_ +#define VERILATOR_V3INSTRUMENT_H_ #include "config_build.h" #include "verilatedos.h" @@ -24,7 +24,7 @@ class AstNetlist; //========================================================================= -class V3Instrumentation final { +class V3Instrument final { public: static void findTargets(AstNetlist* nodep) VL_MT_DISABLED; static void instrument(AstNetlist* nodep) VL_MT_DISABLED; diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 75704a70b..c307b806e 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -65,7 +65,7 @@ #include "V3HierBlock.h" #include "V3Inline.h" #include "V3Inst.h" -#include "V3Instrumentation.h" +#include "V3Instrument.h" #include "V3Interface.h" #include "V3Life.h" #include "V3LifePost.h" @@ -159,9 +159,8 @@ static void process() { // Instrument Design with the configurations given in .vlt file if (v3Global.opt.instrument()) { v3Global.dpi(true); - V3Instrumentation::findTargets(v3Global.rootp()); - V3Error::abortIfErrors(); - V3Instrumentation::instrument(v3Global.rootp()); + V3Instrument::findTargets(v3Global.rootp()); + V3Instrument::instrument(v3Global.rootp()); } // Convert parseref's to varrefs, and other directly post parsing fixups diff --git a/src/verilog.y b/src/verilog.y index 082996b09..5e1ca5da6 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -8014,7 +8014,7 @@ vltItem: | vltInlineFront vltDModuleE vltDFTaskE { V3Control::addInline($1, *$2, *$3, $1); } | yVLT_INSTRUMENT yVLT_D_MODEL yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING - { V3Control::addInstrumentationConfigs($1, *$3, $5->toSInt(), *$7); } + { V3Control::addInstrumentCfg($1, *$3, $5->toSInt(), *$7); } | yVLT_COVERAGE_BLOCK_OFF vltDFile { V3Control::addCoverageBlockOff(*$2, 0); } | yVLT_COVERAGE_BLOCK_OFF vltDFile yVLT_D_LINES yaINTNUM diff --git a/test_regress/t/t_instrumentation.cpp b/test_regress/t/t_instrument.cpp similarity index 93% rename from test_regress/t/t_instrumentation.cpp rename to test_regress/t/t_instrument.cpp index d88813e15..ea67c40b0 100644 --- a/test_regress/t/t_instrumentation.cpp +++ b/test_regress/t/t_instrument.cpp @@ -22,7 +22,7 @@ int main(int argc, char** argv) { std::unique_ptr top{new VM_PREFIX{"top"}}; - std::ofstream logFile("obj_vlt/t_instrumentation/simulation_output.log"); + std::ofstream logFile("obj_vlt/t_instrument/simulation_output.log"); if (!logFile.is_open()) { printf("Error: Could not open log file\n"); return 1; diff --git a/test_regress/t/t_instrumentation.out b/test_regress/t/t_instrument.out similarity index 100% rename from test_regress/t/t_instrumentation.out rename to test_regress/t/t_instrument.out diff --git a/test_regress/t/t_instrumentation.py b/test_regress/t/t_instrument.py similarity index 83% rename from test_regress/t/t_instrumentation.py rename to test_regress/t/t_instrument.py index 83bcaed16..668cf3856 100755 --- a/test_regress/t/t_instrumentation.py +++ b/test_regress/t/t_instrument.py @@ -10,12 +10,12 @@ import vltest_bootstrap test.scenarios('simulator') -test.top_filename = "t/t_instrumentation.v" +test.top_filename = "t/t_instrument.v" sim_filename = "t/" + test.name + ".cpp" -dpi_filename = "t/t_instrumentationDPI.cpp" +dpi_filename = "t/t_instrumentDPI.cpp" vlt_filename = "t/" + test.name + ".vlt" -log_filename = "obj_vlt/t_instrumentation/simulation_output.log" +log_filename = "obj_vlt/t_instrument/simulation_output.log" test.compile(make_top_shell=False, make_main=False, diff --git a/test_regress/t/t_instrumentation.v b/test_regress/t/t_instrument.v similarity index 100% rename from test_regress/t/t_instrumentation.v rename to test_regress/t/t_instrument.v diff --git a/test_regress/t/t_instrumentation.vlt b/test_regress/t/t_instrument.vlt similarity index 100% rename from test_regress/t/t_instrumentation.vlt rename to test_regress/t/t_instrument.vlt diff --git a/test_regress/t/t_instrumentationDPI.cpp b/test_regress/t/t_instrumentDPI.cpp similarity index 100% rename from test_regress/t/t_instrumentationDPI.cpp rename to test_regress/t/t_instrumentDPI.cpp diff --git a/test_regress/t/t_instrument_bad.out b/test_regress/t/t_instrument_bad.out new file mode 100644 index 000000000..136d06384 --- /dev/null +++ b/test_regress/t/t_instrument_bad.out @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/test_regress/t/t_instrument_bad.py b/test_regress/t/t_instrument_bad.py new file mode 100755 index 000000000..c372b22fd --- /dev/null +++ b/test_regress/t/t_instrument_bad.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 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 + +import vltest_bootstrap + +test.scenarios('linter') +test.top_filename = "t/t_instrument.v" +test.flag = "--instrument" + +test.lint(fails=True, expect_filename=test.golden_filename) + +test.passes() \ No newline at end of file diff --git a/test_regress/t/t_instrument_bad.vlt b/test_regress/t/t_instrument_bad.vlt new file mode 100644 index 000000000..1fd81f390 --- /dev/null +++ b/test_regress/t/t_instrument_bad.vlt @@ -0,0 +1,17 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`verilator_config + +instrument -callback "instrument_var" -id 0 -target "top_module.outa" + +instrument -callback "instrument_var" -id 0 -target "top.a1.b1.out" + +instrument -callback "instrument_var" -id 0 -target "top_module.a3.b1.out" + +instrument -callback "instrument_var" -id 0 -target "top_module.a1.b3.out" + +instrument -callback "instrument_var" -id 0 -target "top_module.a1.b1.clk" From dccd630fc8686741b0891f53c675dcb1d329941d Mon Sep 17 00:00:00 2001 From: github action Date: Thu, 9 Oct 2025 09:18:34 +0000 Subject: [PATCH 06/20] Apply 'make format' --- src/V3Instrument.cpp | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index ee3f02c65..e2834eee8 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -453,10 +453,10 @@ class InstrumentTargetFndr final : public VNVisitor { } m_foundVarp = true; } else if (nodep->nextp() == nullptr && !entry.found) { - v3error("Verilator-configfile': could not find defined 'var' in " - "'topModule.instance.var' ... Target string: '" - << m_target+"."+entry.varTarget << "'"); - return; + v3error("Verilator-configfile': could not find defined 'var' in " + "'topModule.instance.var' ... Target string: '" + << m_target + "." + entry.varTarget << "'"); + return; } } } @@ -606,19 +606,17 @@ class InstrumentFunc final : public VNVisitor { return false; } } - // Check if the module and instances defined in the target string were found in + // Check if the module and instances defined in the target string were found in // the previous step - bool hasNullptr(const std::pair &pair) { + bool hasNullptr(const std::pair& pair) { bool moduleNullptr = pair.second.origModulep == nullptr; bool cellNullptr = pair.second.cellp == nullptr; return moduleNullptr || cellNullptr; } - // Check if the, in the target string, defined variable was found in the previous step - bool isFound(const std::pair &pair) { + // Check if the, in the target string, defined variable was found in the previous step + bool isFound(const std::pair& pair) { for (auto& entry : pair.second.entries) { - if (entry.found == false) { - return entry.found; - } + if (entry.found == false) { return entry.found; } } return true; } @@ -689,10 +687,12 @@ class InstrumentFunc final : public VNVisitor { void visit(AstNetlist* nodep) override { const auto& instrCfg = V3Control::getInstrumentCfg(); for (const auto& pair : instrCfg) { - if (hasNullptr(pair) || !isFound(pair)) { - v3error("Verilator-configfile: Incomplete instrumentation configuration for target '" - << pair.first<< "'. Please check previous Errors from V3Instrument:findTargets and ensure" - << " all necessary components are correct defined."); + if (hasNullptr(pair) || !isFound(pair)) { + v3error( + "Verilator-configfile: Incomplete instrumentation configuration for target '" + << pair.first + << "'. Please check previous Errors from V3Instrument:findTargets and ensure" + << " all necessary components are correct defined."); } else { nodep->addModulesp(pair.second.instrModulep); m_targetKey = pair.first; From 209e3548bfc101e28ab59ed46bc3618869f2d90d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 22 Oct 2025 08:50:24 +0200 Subject: [PATCH 07/20] fix: Changed instrumentation directive from model to callback --- src/verilog.l | 2 +- src/verilog.y | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/verilog.l b/src/verilog.l index 1aec846cc..eff7e8d50 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -145,13 +145,13 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "tracing_on" { FL; return yVLT_TRACING_ON; } -?"-block" { FL; return yVLT_D_BLOCK; } + -?"-callback" { FL; return yVLT_D_CALLBACK; } -?"-contents" { FL; return yVLT_D_CONTENTS; } -?"-cost" { FL; return yVLT_D_COST; } -?"-file" { FL; return yVLT_D_FILE; } -?"-function" { FL; return yVLT_D_FUNCTION; } -?"-hier-dpi" { FL; return yVLT_D_HIER_DPI; } -?"-id" { FL; return yVLT_D_ID; } - -?"-instance" { FL; return yVLT_D_INSTANCE; } -?"-levels" { FL; return yVLT_D_LEVELS; } -?"-lines" { FL; return yVLT_D_LINES; } -?"-match" { FL; return yVLT_D_MATCH; } diff --git a/src/verilog.y b/src/verilog.y index 5e1ca5da6..93276c0a2 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -272,13 +272,13 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_TRACING_ON "tracing_on" %token yVLT_D_BLOCK "--block" +%token yVLT_D_CALLBACK "--callback" %token yVLT_D_CONTENTS "--contents" %token yVLT_D_COST "--cost" %token yVLT_D_FILE "--file" %token yVLT_D_FUNCTION "--function" %token yVLT_D_HIER_DPI "--hier-dpi" %token yVLT_D_ID "--id" -%token yVLT_D_INSTANCE "--instance" %token yVLT_D_LEVELS "--levels" %token yVLT_D_LINES "--lines" %token yVLT_D_MATCH "--match" @@ -8013,7 +8013,7 @@ vltItem: { /* Historical, now has no effect */ } | vltInlineFront vltDModuleE vltDFTaskE { V3Control::addInline($1, *$2, *$3, $1); } - | yVLT_INSTRUMENT yVLT_D_MODEL yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING + | yVLT_INSTRUMENT yVLT_D_CALLBACK yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING { V3Control::addInstrumentCfg($1, *$3, $5->toSInt(), *$7); } | yVLT_COVERAGE_BLOCK_OFF vltDFile { V3Control::addCoverageBlockOff(*$2, 0); } From 0091374c166228ede88ebaa3f59edb201b535c6a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 22 Oct 2025 08:53:52 +0200 Subject: [PATCH 08/20] fix: Enabled DPI-Calls when design is stale/constant --- src/V3Instrument.cpp | 47 ++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 45 insertions(+), 2 deletions(-) diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index e2834eee8..08aee1184 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -507,6 +507,7 @@ class InstrumentFunc final : public VNVisitor { AstTask* m_taskp = nullptr; // // Stores the created task node AstFunc* m_funcp = nullptr; // Stores the created function node AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node + AstLoop* m_loopp = nullptr; // Stores the created loop pointer AstTaskRef* m_taskrefp = nullptr; // Stores the created taskref node AstModule* m_current_module = nullptr; // Stores the currenty visited module AstModule* m_current_module_cell_check @@ -515,6 +516,9 @@ class InstrumentFunc final : public VNVisitor { AstVar* m_orig_varp = nullptr; // Stores the original variable node AstVar* m_orig_varp_instMod = nullptr; // Stores the original variable node in instrumented module node + AstVar* m_dpi_trigger + = nullptr; // Stores the variable noded for the dpi-trigger, which ensures the changing of + // a signal and the execution of the DPI function AstPort* m_orig_portp = nullptr; // Stores the original port node // METHODS @@ -766,7 +770,15 @@ class InstrumentFunc final : public VNVisitor { m_tmp_varp->trace(true); } nodep->addStmtsp(m_tmp_varp); - + m_dpi_trigger = new AstVar{ + nodep->fileline(), VVarType::VAR, "dpi_trigger", VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT, VSigning::NOSIGN}}; + m_dpi_trigger->trace(false); + nodep->addStmtsp(m_dpi_trigger); + m_loopp = new AstLoop{nodep->fileline()}; + AstInitial* initialp = new AstInitial{ + nodep->fileline(), new AstBegin{nodep->fileline(), "", m_loopp, false}}; + nodep->addStmtsp(initialp); if (m_taskp != nullptr) { m_taskrefp = new AstTaskRef{ nodep->fileline(), m_task_name, @@ -786,7 +798,6 @@ class InstrumentFunc final : public VNVisitor { m_funcrefp}; nodep->addStmtsp(m_assignwp); } - if (m_targetIndex == entries.size() - 1) { setDone(nodep); } for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { if (VN_IS(n, Port)) { m_pinnum = VN_CAST(n, Port)->pinNum(); } @@ -827,6 +838,8 @@ class InstrumentFunc final : public VNVisitor { m_addedFunc = false; m_addedport = false; m_instGenBlock = nullptr; + m_dpi_trigger = nullptr; + m_loopp = nullptr; } m_targetIndex = 0; } @@ -928,6 +941,7 @@ class InstrumentFunc final : public VNVisitor { void visit(AstFunc* nodep) { if (m_addedFunc == false && nodep == m_funcp && m_current_module != nullptr) { AstVar* instrID = nullptr; + AstVar* dpi_trigger = nullptr; AstVar* var_x_func = nullptr; instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, @@ -938,12 +952,37 @@ class InstrumentFunc final : public VNVisitor { var_x_func = m_orig_varp->cloneTree(false); var_x_func->varType(VVarType::PORT); var_x_func->direction(VDirection::INPUT); + dpi_trigger = m_dpi_trigger->cloneTree(false); + dpi_trigger->varType(VVarType::PORT); + dpi_trigger->direction(VDirection::INPUT); nodep->addStmtsp(instrID); + nodep->addStmtsp(dpi_trigger); nodep->addStmtsp(var_x_func); } } + //ASTLOOP VISITOR FUNCTION: + //The function is used to add the logic for the dpi trigger, that triggers the DPI function at + //the smallest possible time intervals. + void visit(AstLoop* nodep) override { + if (nodep == m_loopp && m_current_module != nullptr) { + AstParseRef* initialParseRefrhsp + = new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_dpi_trigger->name()}; + AstParseRef* initialParseReflhsp + = new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_dpi_trigger->name()}; + AstBegin* initialBeginp = new AstBegin{ + nodep->fileline(), "", + new AstAssign{nodep->fileline(), initialParseReflhsp, + new AstLogNot{nodep->fileline(), initialParseRefrhsp}}, + false}; + initialBeginp->addStmtsp( + new AstDelay{nodep->fileline(), + new AstConst{nodep->fileline(), AstConst::Unsized32{}, 1}, false}); + nodep->addContsp(initialBeginp); + } + } + //ASTALWAYS VISITOR FUNCTION: //The function is used to add the task reference node to the always node and further specify //the always node. @@ -998,10 +1037,14 @@ class InstrumentFunc final : public VNVisitor { nodep->fileline(), AstConst::Unsized32{}, static_cast(getMapEntryFaultCase(m_targetKey, m_targetIndex))}; + AstVarRef* added_triggerp + = new AstVarRef{nodep->fileline(), m_dpi_trigger, VAccess::READ}; + AstVarRef* added_varrefp = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); + nodep->addPinsp(new AstArg{nodep->fileline(), "", added_triggerp}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); m_orig_varp_instMod = nullptr; } From f50d72eb9e20600ee1cbe495be65dd14293a3c79 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 22 Oct 2025 08:58:39 +0200 Subject: [PATCH 09/20] test: Updated regression test for instrumentation --- test_regress/t/t_instrument.cpp | 35 ++++-- test_regress/t/t_instrument.out | 183 +++++++++++++++-------------- test_regress/t/t_instrument.py | 12 +- test_regress/t/t_instrument.v | 14 +-- test_regress/t/t_instrument.vlt | 4 +- test_regress/t/t_instrumentDPI.cpp | 21 ++-- 6 files changed, 142 insertions(+), 127 deletions(-) diff --git a/test_regress/t/t_instrument.cpp b/test_regress/t/t_instrument.cpp index ea67c40b0..ef8af0cf7 100644 --- a/test_regress/t/t_instrument.cpp +++ b/test_regress/t/t_instrument.cpp @@ -9,34 +9,43 @@ #include #include -#include +#include #include VM_PREFIX_INCLUDE -unsigned long long main_time = 0; +vluint64_t main_time = 0; double sc_time_stamp() { return main_time; } int main(int argc, char** argv) { Verilated::debug(0); Verilated::commandArgs(argc, argv); - std::unique_ptr top{new VM_PREFIX{"top"}}; - - std::ofstream logFile("obj_vlt/t_instrument/simulation_output.log"); - if (!logFile.is_open()) { - printf("Error: Could not open log file\n"); - return 1; - } + const std::unique_ptr top{new VM_PREFIX{"TOP"}}; while (main_time <= 100) { + if (main_time < 20) { + top->in1a = 5; + top->in2a = 10; + top->in1b = 20; + top->in2b = 30; + } else if (main_time >= 20 && main_time < 63) { + top->in1a = 0; + top->in2a = 5; + top->in1b = 15; + top->in2b = 25; + } else if (main_time > 78) { + top->in1a = 10; + top->in2a = 15; + top->in1b = 25; + top->in2b = 35; + } top->eval(); - logFile << "$time: " << main_time << " | " - << "Output outa: " << static_cast(top->outa) << " | " - << "Output outb: " << static_cast(top->outb) << std::endl; + std::cout << "$time: " << main_time << " | " + << "Output outa: " << static_cast(top->outa) << " | " + << "Output outb: " << static_cast(top->outb) << std::endl; ++main_time; } top->final(); - top.reset(); printf("*-* All Finished *-*\n"); return 0; } diff --git a/test_regress/t/t_instrument.out b/test_regress/t/t_instrument.out index 7ba39071f..7f87814a2 100644 --- a/test_regress/t/t_instrument.out +++ b/test_regress/t/t_instrument.out @@ -1,13 +1,13 @@ -$time: 0 | Output outa: 0 | Output outb: 50 -$time: 1 | Output outa: 0 | Output outb: 50 -$time: 2 | Output outa: 0 | Output outb: 50 -$time: 3 | Output outa: 0 | Output outb: 50 -$time: 4 | Output outa: 0 | Output outb: 50 -$time: 5 | Output outa: 0 | Output outb: 50 -$time: 6 | Output outa: 0 | Output outb: 50 -$time: 7 | Output outa: 0 | Output outb: 50 -$time: 8 | Output outa: 0 | Output outb: 50 -$time: 9 | Output outa: 0 | Output outb: 50 +$time: 0 | Output outa: 15 | Output outb: 1 +$time: 1 | Output outa: 15 | Output outb: 1 +$time: 2 | Output outa: 15 | Output outb: 1 +$time: 3 | Output outa: 15 | Output outb: 50 +$time: 4 | Output outa: 15 | Output outb: 50 +$time: 5 | Output outa: 15 | Output outb: 50 +$time: 6 | Output outa: 15 | Output outb: 50 +$time: 7 | Output outa: 15 | Output outb: 50 +$time: 8 | Output outa: 15 | Output outb: 50 +$time: 9 | Output outa: 15 | Output outb: 50 $time: 10 | Output outa: 0 | Output outb: 50 $time: 11 | Output outa: 0 | Output outb: 50 $time: 12 | Output outa: 0 | Output outb: 50 @@ -18,84 +18,85 @@ $time: 16 | Output outa: 0 | Output outb: 50 $time: 17 | Output outa: 0 | Output outb: 50 $time: 18 | Output outa: 0 | Output outb: 50 $time: 19 | Output outa: 0 | Output outb: 50 -$time: 20 | Output outa: 0 | Output outb: 50 -$time: 21 | Output outa: 0 | Output outb: 50 -$time: 22 | Output outa: 0 | Output outb: 50 -$time: 23 | Output outa: 0 | Output outb: 50 -$time: 24 | Output outa: 0 | Output outb: 50 -$time: 25 | Output outa: 0 | Output outb: 50 -$time: 26 | Output outa: 0 | Output outb: 50 -$time: 27 | Output outa: 0 | Output outb: 50 -$time: 28 | Output outa: 0 | Output outb: 50 -$time: 29 | Output outa: 0 | Output outb: 50 -$time: 30 | Output outa: 0 | Output outb: 50 -$time: 31 | Output outa: 0 | Output outb: 50 -$time: 32 | Output outa: 0 | Output outb: 50 -$time: 33 | Output outa: 0 | Output outb: 50 -$time: 34 | Output outa: 0 | Output outb: 50 -$time: 35 | Output outa: 0 | Output outb: 50 -$time: 36 | Output outa: 0 | Output outb: 50 -$time: 37 | Output outa: 0 | Output outb: 50 -$time: 38 | Output outa: 0 | Output outb: 50 -$time: 39 | Output outa: 0 | Output outb: 50 -$time: 40 | Output outa: 0 | Output outb: 50 -$time: 41 | Output outa: 0 | Output outb: 50 -$time: 42 | Output outa: 0 | Output outb: 50 -$time: 43 | Output outa: 0 | Output outb: 50 -$time: 44 | Output outa: 0 | Output outb: 50 -$time: 45 | Output outa: 0 | Output outb: 50 -$time: 46 | Output outa: 0 | Output outb: 50 -$time: 47 | Output outa: 0 | Output outb: 50 -$time: 48 | Output outa: 0 | Output outb: 50 -$time: 49 | Output outa: 0 | Output outb: 50 -$time: 50 | Output outa: 0 | Output outb: 50 -$time: 51 | Output outa: 0 | Output outb: 50 -$time: 52 | Output outa: 0 | Output outb: 50 -$time: 53 | Output outa: 0 | Output outb: 50 -$time: 54 | Output outa: 0 | Output outb: 50 -$time: 55 | Output outa: 0 | Output outb: 50 -$time: 56 | Output outa: 0 | Output outb: 50 -$time: 57 | Output outa: 0 | Output outb: 50 -$time: 58 | Output outa: 0 | Output outb: 50 -$time: 59 | Output outa: 0 | Output outb: 50 -$time: 60 | Output outa: 0 | Output outb: 50 -$time: 61 | Output outa: 0 | Output outb: 50 -$time: 62 | Output outa: 0 | Output outb: 50 -$time: 63 | Output outa: 0 | Output outb: 50 -$time: 64 | Output outa: 0 | Output outb: 50 -$time: 65 | Output outa: 0 | Output outb: 50 -$time: 66 | Output outa: 0 | Output outb: 50 -$time: 67 | Output outa: 0 | Output outb: 50 -$time: 68 | Output outa: 0 | Output outb: 50 -$time: 69 | Output outa: 0 | Output outb: 50 -$time: 70 | Output outa: 0 | Output outb: 50 -$time: 71 | Output outa: 0 | Output outb: 50 -$time: 72 | Output outa: 0 | Output outb: 50 -$time: 73 | Output outa: 0 | Output outb: 50 -$time: 74 | Output outa: 0 | Output outb: 50 -$time: 75 | Output outa: 0 | Output outb: 50 -$time: 76 | Output outa: 0 | Output outb: 50 -$time: 77 | Output outa: 0 | Output outb: 50 -$time: 78 | Output outa: 0 | Output outb: 50 -$time: 79 | Output outa: 0 | Output outb: 50 -$time: 80 | Output outa: 0 | Output outb: 50 -$time: 81 | Output outa: 0 | Output outb: 50 -$time: 82 | Output outa: 0 | Output outb: 50 -$time: 83 | Output outa: 0 | Output outb: 50 -$time: 84 | Output outa: 0 | Output outb: 50 -$time: 85 | Output outa: 0 | Output outb: 50 -$time: 86 | Output outa: 0 | Output outb: 50 -$time: 87 | Output outa: 0 | Output outb: 50 -$time: 88 | Output outa: 0 | Output outb: 50 -$time: 89 | Output outa: 0 | Output outb: 50 -$time: 90 | Output outa: 0 | Output outb: 50 -$time: 91 | Output outa: 0 | Output outb: 50 -$time: 92 | Output outa: 0 | Output outb: 50 -$time: 93 | Output outa: 0 | Output outb: 50 -$time: 94 | Output outa: 0 | Output outb: 50 -$time: 95 | Output outa: 0 | Output outb: 50 -$time: 96 | Output outa: 0 | Output outb: 50 -$time: 97 | Output outa: 0 | Output outb: 50 -$time: 98 | Output outa: 0 | Output outb: 50 -$time: 99 | Output outa: 0 | Output outb: 50 -$time: 100 | Output outa: 0 | Output outb: 50 +$time: 20 | Output outa: 5 | Output outb: 40 +$time: 21 | Output outa: 5 | Output outb: 40 +$time: 22 | Output outa: 5 | Output outb: 40 +$time: 23 | Output outa: 5 | Output outb: 40 +$time: 24 | Output outa: 5 | Output outb: 40 +$time: 25 | Output outa: 5 | Output outb: 40 +$time: 26 | Output outa: 5 | Output outb: 40 +$time: 27 | Output outa: 5 | Output outb: 40 +$time: 28 | Output outa: 5 | Output outb: 40 +$time: 29 | Output outa: 5 | Output outb: 40 +$time: 30 | Output outa: 5 | Output outb: 40 +$time: 31 | Output outa: 5 | Output outb: 40 +$time: 32 | Output outa: 5 | Output outb: 1 +$time: 33 | Output outa: 5 | Output outb: 1 +$time: 34 | Output outa: 5 | Output outb: 1 +$time: 35 | Output outa: 5 | Output outb: 1 +$time: 36 | Output outa: 5 | Output outb: 1 +$time: 37 | Output outa: 5 | Output outb: 1 +$time: 38 | Output outa: 5 | Output outb: 1 +$time: 39 | Output outa: 5 | Output outb: 1 +$time: 40 | Output outa: 5 | Output outb: 1 +$time: 41 | Output outa: 5 | Output outb: 1 +$time: 42 | Output outa: 5 | Output outb: 1 +$time: 43 | Output outa: 5 | Output outb: 1 +$time: 44 | Output outa: 5 | Output outb: 1 +$time: 45 | Output outa: 5 | Output outb: 1 +$time: 46 | Output outa: 5 | Output outb: 1 +$time: 47 | Output outa: 5 | Output outb: 1 +$time: 48 | Output outa: 5 | Output outb: 1 +$time: 49 | Output outa: 5 | Output outb: 1 +$time: 50 | Output outa: 5 | Output outb: 1 +$time: 51 | Output outa: 5 | Output outb: 1 +$time: 52 | Output outa: 5 | Output outb: 1 +$time: 53 | Output outa: 5 | Output outb: 1 +$time: 54 | Output outa: 5 | Output outb: 1 +$time: 55 | Output outa: 5 | Output outb: 1 +$time: 56 | Output outa: 5 | Output outb: 1 +$time: 57 | Output outa: 5 | Output outb: 1 +$time: 58 | Output outa: 5 | Output outb: 1 +$time: 59 | Output outa: 5 | Output outb: 1 +$time: 60 | Output outa: 5 | Output outb: 1 +$time: 61 | Output outa: 5 | Output outb: 1 +$time: 62 | Output outa: 5 | Output outb: 1 +$time: 63 | Output outa: 5 | Output outb: 1 +$time: 64 | Output outa: 5 | Output outb: 1 +$time: 65 | Output outa: 5 | Output outb: 1 +$time: 66 | Output outa: 5 | Output outb: 1 +$time: 67 | Output outa: 5 | Output outb: 1 +$time: 68 | Output outa: 5 | Output outb: 1 +$time: 69 | Output outa: 5 | Output outb: 40 +$time: 70 | Output outa: 5 | Output outb: 40 +$time: 71 | Output outa: 5 | Output outb: 40 +$time: 72 | Output outa: 5 | Output outb: 40 +$time: 73 | Output outa: 5 | Output outb: 40 +$time: 74 | Output outa: 5 | Output outb: 40 +$time: 75 | Output outa: 5 | Output outb: 40 +$time: 76 | Output outa: 5 | Output outb: 40 +$time: 77 | Output outa: 5 | Output outb: 40 +$time: 78 | Output outa: 5 | Output outb: 40 +$time: 79 | Output outa: 25 | Output outb: 60 +$time: 80 | Output outa: 25 | Output outb: 60 +$time: 81 | Output outa: 25 | Output outb: 60 +$time: 82 | Output outa: 25 | Output outb: 60 +$time: 83 | Output outa: 25 | Output outb: 60 +$time: 84 | Output outa: 25 | Output outb: 60 +$time: 85 | Output outa: 0 | Output outb: 60 +$time: 86 | Output outa: 0 | Output outb: 60 +$time: 87 | Output outa: 0 | Output outb: 60 +$time: 88 | Output outa: 0 | Output outb: 60 +$time: 89 | Output outa: 0 | Output outb: 60 +$time: 90 | Output outa: 0 | Output outb: 60 +$time: 91 | Output outa: 0 | Output outb: 60 +$time: 92 | Output outa: 0 | Output outb: 60 +$time: 93 | Output outa: 0 | Output outb: 60 +$time: 94 | Output outa: 0 | Output outb: 60 +$time: 95 | Output outa: 0 | Output outb: 60 +$time: 96 | Output outa: 0 | Output outb: 60 +$time: 97 | Output outa: 0 | Output outb: 60 +$time: 98 | Output outa: 0 | Output outb: 60 +$time: 99 | Output outa: 0 | Output outb: 60 +$time: 100 | Output outa: 0 | Output outb: 60 +*-* All Finished *-* diff --git a/test_regress/t/t_instrument.py b/test_regress/t/t_instrument.py index 668cf3856..34ef9eccc 100755 --- a/test_regress/t/t_instrument.py +++ b/test_regress/t/t_instrument.py @@ -15,13 +15,11 @@ test.top_filename = "t/t_instrument.v" sim_filename = "t/" + test.name + ".cpp" dpi_filename = "t/t_instrumentDPI.cpp" vlt_filename = "t/" + test.name + ".vlt" -log_filename = "obj_vlt/t_instrument/simulation_output.log" -test.compile(make_top_shell=False, - make_main=False, - v_flags2=["--trace --exe --instrument", sim_filename, vlt_filename, dpi_filename]) -test.execute() - -test.files_identical(log_filename, test.golden_filename) +test.compile( + make_top_shell=False, + make_main=False, + v_flags2=["--trace --timing --exe --instrument", sim_filename, vlt_filename, dpi_filename]) +test.execute(expect_filename=test.golden_filename) test.passes() diff --git a/test_regress/t/t_instrument.v b/test_regress/t/t_instrument.v index 674f8293e..5a7a8a2b8 100644 --- a/test_regress/t/t_instrument.v +++ b/test_regress/t/t_instrument.v @@ -5,17 +5,17 @@ // SPDX-License-Identifier: CC0-1.0 module top_module( - output logic [7:0] outa, - output logic [7:0] outb + input reg [7:0] in1a, + input reg [7:0] in2a, + input reg [7:0] in1b, + input reg [7:0] in2b, + output logic [7:0] outa, + output logic [7:0] outb ); - logic [7:0] in1a = 8'd5; - logic [7:0] in2a = 8'd10; - - logic [7:0] in1b = 8'd20; - logic [7:0] in2b = 8'd30; module_a a1 (.in1(in1a), .in2(in2a), .out(outa)); module_a a2 (.in1(in1b), .in2(in2b), .out(outb)); + endmodule module module_a( diff --git a/test_regress/t/t_instrument.vlt b/test_regress/t/t_instrument.vlt index e96535c09..b0b8351f4 100644 --- a/test_regress/t/t_instrument.vlt +++ b/test_regress/t/t_instrument.vlt @@ -6,4 +6,6 @@ `verilator_config -instrument -model "instrument_var" -id 0 -target "top_module.a1.b1.out" +instrument -callback "instrument_var" -id 0 -target "top_module.a1.b1.out" + +instrument -callback "instrument_var" -id 1 -target "top_module.a2.out" diff --git a/test_regress/t/t_instrumentDPI.cpp b/test_regress/t/t_instrumentDPI.cpp index 78eb0a723..b5e565435 100644 --- a/test_regress/t/t_instrumentDPI.cpp +++ b/test_regress/t/t_instrumentDPI.cpp @@ -8,18 +8,23 @@ #include -#include #include -extern "C" short instrument_var(int id, const svLogic* x) { +extern "C" int instrument_var(int id, int trigger, const svLogic* x) { switch (id) { - case 0: return 0; + case 0: + if ((VL_TIME_Q() >= 10 && VL_TIME_Q() < 20) || VL_TIME_Q() >= 85) { + return 0; + } else { + return *x; + } + //return 0; case 1: - // Stuck at 1 Fault Injection - return 1; - case 2: - // Inverter/Bit flip Fault injection (provisional) - return !x; + if ((VL_TIME_Q() >= 0 && VL_TIME_Q() < 3) || (VL_TIME_Q() >= 32 && VL_TIME_Q() < 69)) { + return 1; + } else { + return *x; + } default: return *x; } } From 37ad722bb88cb2ed27f7a629efc39b949bebcb1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 22 Oct 2025 11:28:38 +0200 Subject: [PATCH 10/20] test: Added test for failing instrumentation --- test_regress/t/t_instrument.v | 1 + test_regress/t/t_instrument_bad.out | 1 - test_regress/t/t_instrument_bad1.out | 2 ++ ...instrument_bad.py => t_instrument_bad1.py} | 14 +++++++---- test_regress/t/t_instrument_bad1.vlt | 9 +++++++ test_regress/t/t_instrument_bad2.out | 12 ++++++++++ test_regress/t/t_instrument_bad2.py | 24 +++++++++++++++++++ ...strument_bad.vlt => t_instrument_bad2.vlt} | 4 ++-- 8 files changed, 60 insertions(+), 7 deletions(-) delete mode 100644 test_regress/t/t_instrument_bad.out create mode 100644 test_regress/t/t_instrument_bad1.out rename test_regress/t/{t_instrument_bad.py => t_instrument_bad1.py} (54%) create mode 100644 test_regress/t/t_instrument_bad1.vlt create mode 100644 test_regress/t/t_instrument_bad2.out create mode 100755 test_regress/t/t_instrument_bad2.py rename test_regress/t/{t_instrument_bad.vlt => t_instrument_bad2.vlt} (96%) diff --git a/test_regress/t/t_instrument.v b/test_regress/t/t_instrument.v index 5a7a8a2b8..71471aed9 100644 --- a/test_regress/t/t_instrument.v +++ b/test_regress/t/t_instrument.v @@ -31,6 +31,7 @@ module module_b ( input logic [7:0] in2, output logic [7:0] out ); + reg [127:0] bigRegister; always_comb begin out = in1 + in2; end diff --git a/test_regress/t/t_instrument_bad.out b/test_regress/t/t_instrument_bad.out deleted file mode 100644 index 136d06384..000000000 --- a/test_regress/t/t_instrument_bad.out +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/test_regress/t/t_instrument_bad1.out b/test_regress/t/t_instrument_bad1.out new file mode 100644 index 000000000..e228e30a1 --- /dev/null +++ b/test_regress/t/t_instrument_bad1.out @@ -0,0 +1,2 @@ +%Error: In .vlt defined target tries to instrument the highest MODULE, is not possible! ... Target string: top_module.outa + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. diff --git a/test_regress/t/t_instrument_bad.py b/test_regress/t/t_instrument_bad1.py similarity index 54% rename from test_regress/t/t_instrument_bad.py rename to test_regress/t/t_instrument_bad1.py index c372b22fd..e16d792f7 100755 --- a/test_regress/t/t_instrument_bad.py +++ b/test_regress/t/t_instrument_bad1.py @@ -9,10 +9,16 @@ import vltest_bootstrap -test.scenarios('linter') +test.scenarios('simulator') test.top_filename = "t/t_instrument.v" -test.flag = "--instrument" -test.lint(fails=True, expect_filename=test.golden_filename) +sim_filename = "t/t_instrument.cpp" +dpi_filename = "t/t_instrumentDPI.cpp" +vlt_filename = "t/" + test.name + ".vlt" -test.passes() \ No newline at end of file +test.compile(fails=True, make_top_shell=False, + make_main=False, + v_flags2=["--trace --timing --exe --instrument", sim_filename, vlt_filename, dpi_filename], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_instrument_bad1.vlt b/test_regress/t/t_instrument_bad1.vlt new file mode 100644 index 000000000..2a34c9bd0 --- /dev/null +++ b/test_regress/t/t_instrument_bad1.vlt @@ -0,0 +1,9 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed into the Public Domain, for any use, +// without warranty, 2025 by Wilson Snyder. +// SPDX-License-Identifier: CC0-1.0 + +`verilator_config + +instrument -callback "instrument_var" -id 0 -target "top_module.outa" diff --git a/test_regress/t/t_instrument_bad2.out b/test_regress/t/t_instrument_bad2.out new file mode 100644 index 000000000..9b6c5cc9f --- /dev/null +++ b/test_regress/t/t_instrument_bad2.out @@ -0,0 +1,12 @@ +%Error: Verilator-configfile': could not find initial 'module' in 'module.instance.__' ... Target: 'top.a1.b1' + ... See the manual at https://verilator.org/verilator_doc.html?v=latest for more assistance. +%Error: Verilator-configfile: target variable 'bigRegister' in 'top_module.a1.b1' must be a supported type! +%Error: Verilator-configfile': could not find defined 'var' in 'topModule.instance.var' ... Target string: 'top_module.a1.b1.clk' +%Error: Verilator-configfile': could not find '.var' in '__.module.var' ... Target: 'top_module.a1.b1' +%Error: Verilator-configfile: could not find 'instance' in '__.instance.__' ... Target string: 'top_module.a1.b3' +%Error: Verilator-configfile': could not find initial 'instance' in 'topModule.instance.__' ... Target string: 'top_module.a3.b1' +%Error: Verilator-configfile: Incomplete instrumentation configuration for target 'top.a1.b1'. Please check previous Errors from V3Instrument:findTargets and ensure all necessary components are correct defined. +%Error: Verilator-configfile: Incomplete instrumentation configuration for target 'top_module.a1.b1'. Please check previous Errors from V3Instrument:findTargets and ensure all necessary components are correct defined. +%Error: Verilator-configfile: Incomplete instrumentation configuration for target 'top_module.a1.b3'. Please check previous Errors from V3Instrument:findTargets and ensure all necessary components are correct defined. +%Error: Verilator-configfile: Incomplete instrumentation configuration for target 'top_module.a3.b1'. Please check previous Errors from V3Instrument:findTargets and ensure all necessary components are correct defined. +%Error: Exiting due to diff --git a/test_regress/t/t_instrument_bad2.py b/test_regress/t/t_instrument_bad2.py new file mode 100755 index 000000000..e16d792f7 --- /dev/null +++ b/test_regress/t/t_instrument_bad2.py @@ -0,0 +1,24 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 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 + +import vltest_bootstrap + +test.scenarios('simulator') +test.top_filename = "t/t_instrument.v" + +sim_filename = "t/t_instrument.cpp" +dpi_filename = "t/t_instrumentDPI.cpp" +vlt_filename = "t/" + test.name + ".vlt" + +test.compile(fails=True, make_top_shell=False, + make_main=False, + v_flags2=["--trace --timing --exe --instrument", sim_filename, vlt_filename, dpi_filename], + expect_filename=test.golden_filename) + +test.passes() diff --git a/test_regress/t/t_instrument_bad.vlt b/test_regress/t/t_instrument_bad2.vlt similarity index 96% rename from test_regress/t/t_instrument_bad.vlt rename to test_regress/t/t_instrument_bad2.vlt index 1fd81f390..cad5d1bfe 100644 --- a/test_regress/t/t_instrument_bad.vlt +++ b/test_regress/t/t_instrument_bad2.vlt @@ -6,8 +6,6 @@ `verilator_config -instrument -callback "instrument_var" -id 0 -target "top_module.outa" - instrument -callback "instrument_var" -id 0 -target "top.a1.b1.out" instrument -callback "instrument_var" -id 0 -target "top_module.a3.b1.out" @@ -15,3 +13,5 @@ instrument -callback "instrument_var" -id 0 -target "top_module.a3.b1.out" instrument -callback "instrument_var" -id 0 -target "top_module.a1.b3.out" instrument -callback "instrument_var" -id 0 -target "top_module.a1.b1.clk" + +instrument -callback "instrument_var" -id 0 -target "top_module.a1.b1.bigRegister" From 3fcc3a9b42f90b6159b10011ef2d18f46aba9867 Mon Sep 17 00:00:00 2001 From: github action Date: Wed, 22 Oct 2025 10:24:06 +0000 Subject: [PATCH 11/20] Apply 'make format' --- test_regress/t/t_instrument_bad1.py | 4 +++- test_regress/t/t_instrument_bad2.py | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/test_regress/t/t_instrument_bad1.py b/test_regress/t/t_instrument_bad1.py index e16d792f7..9136599a6 100755 --- a/test_regress/t/t_instrument_bad1.py +++ b/test_regress/t/t_instrument_bad1.py @@ -16,7 +16,9 @@ sim_filename = "t/t_instrument.cpp" dpi_filename = "t/t_instrumentDPI.cpp" vlt_filename = "t/" + test.name + ".vlt" -test.compile(fails=True, make_top_shell=False, +test.compile( + fails=True, + make_top_shell=False, make_main=False, v_flags2=["--trace --timing --exe --instrument", sim_filename, vlt_filename, dpi_filename], expect_filename=test.golden_filename) diff --git a/test_regress/t/t_instrument_bad2.py b/test_regress/t/t_instrument_bad2.py index e16d792f7..9136599a6 100755 --- a/test_regress/t/t_instrument_bad2.py +++ b/test_regress/t/t_instrument_bad2.py @@ -16,7 +16,9 @@ sim_filename = "t/t_instrument.cpp" dpi_filename = "t/t_instrumentDPI.cpp" vlt_filename = "t/" + test.name + ".vlt" -test.compile(fails=True, make_top_shell=False, +test.compile( + fails=True, + make_top_shell=False, make_main=False, v_flags2=["--trace --timing --exe --instrument", sim_filename, vlt_filename, dpi_filename], expect_filename=test.golden_filename) From 2a9c91a06bda750b88b37f9530dc0c0db8910a59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 22 Oct 2025 12:54:52 +0200 Subject: [PATCH 12/20] style: Brackets in V3Instrument.cpp --- src/V3Instrument.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index 08aee1184..266b16c21 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -137,9 +137,9 @@ class InstrumentTargetFndr final : public VNVisitor { } // Helper function for adding the parameters into the tree void addParam(AstModule* modp) { - AstVar* paramp = new AstVar(modp->fileline(), VVarType::GPARAM, "INSTRUMENT", - VFlagChildDType{}, nullptr); - paramp->valuep(new AstConst(modp->fileline(), AstConst::Signed32{}, 0)); + AstVar* paramp = new AstVar{modp->fileline(), VVarType::GPARAM, "INSTRUMENT", + VFlagChildDType{}, nullptr}; + paramp->valuep(new AstConst{modp->fileline(), AstConst::Signed32{}, 0}); paramp->dtypep(paramp->valuep()->dtypep()); paramp->ansi(true); modp->addStmtsp(paramp); @@ -149,16 +149,16 @@ class InstrumentTargetFndr final : public VNVisitor { int pinnum = 0; if (isInstrumentPath) { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp = new AstPin(cellp->fileline(), pinnum + 1, "INSTRUMENT", + AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "INSTRUMENT", // The pin is set to 1 to enable the instrumentation path - new AstConst(cellp->fileline(), AstConst::Signed32{}, 1)); + new AstConst{cellp->fileline(), AstConst::Signed32{}, 1}}; pinp->param(true); cellp->addParamsp(pinp); } else { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp = new AstPin( + AstPin* pinp = new AstPin{ cellp->fileline(), pinnum + 1, "INSTRUMENT", - new AstParseRef(cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT")); + new AstParseRef{cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT"}}; pinp->param(true); cellp->addParamsp(pinp); } From 7a015569c14ffc71a869e512f1a38703f33564c1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Thu, 23 Oct 2025 09:34:55 +0200 Subject: [PATCH 13/20] fix: fixed failing regression tests and addressed and adapted to updates from upstream --- src/V3DumpSignals.cpp | 1 - src/V3Instrument.cpp | 127 +++++++++++++++++------------ test_regress/t/t_instrumentDPI.cpp | 2 +- 3 files changed, 76 insertions(+), 54 deletions(-) diff --git a/src/V3DumpSignals.cpp b/src/V3DumpSignals.cpp index c0feeabf3..50d9e129e 100644 --- a/src/V3DumpSignals.cpp +++ b/src/V3DumpSignals.cpp @@ -39,7 +39,6 @@ class DumpSignals final : public VNVisitor { void processVar(AstVar* varp) { if (varp->basicp() && varp->basicp()->name() != "") { bool hasRangep = varp->basicp()->rangep() != nullptr; - bool isSized = varp->basicp()->widthSized(); if (hasRangep) { std::string varHier = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index 266b16c21..c88e0a5bb 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -156,9 +156,8 @@ class InstrumentTargetFndr final : public VNVisitor { cellp->addParamsp(pinp); } else { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp = new AstPin{ - cellp->fileline(), pinnum + 1, "INSTRUMENT", - new AstParseRef{cellp->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT"}}; + AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "INSTRUMENT", + new AstParseRef{cellp->fileline(), "INSTRUMENT"}}; pinp->param(true); cellp->addParamsp(pinp); } @@ -269,7 +268,7 @@ class InstrumentTargetFndr final : public VNVisitor { //conditions the module nodes are added to the instrumentation configs map. Independetly from //these conditions the INSTRUMENT parameter is added to the module nodes in the target path. //This parameter is used to control the instrumentation of the target. - void visit(AstModule* nodep) { + void visit(AstModule* nodep) override { if (m_initModp) { if (targetHasTop(nodep->name(), m_target)) { m_foundModp = true; @@ -366,7 +365,7 @@ class InstrumentTargetFndr final : public VNVisitor { //the cell name fully matches a target path, with the last two entrances removed (Module, Var). //This function ensures that the correct cells in the design hierarchy are instrumented and //tracked, supporting both unique and repeated module instances. - void visit(AstCell* nodep) { + void visit(AstCell* nodep) override { if (m_initModp) { if (targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; @@ -416,13 +415,13 @@ class InstrumentTargetFndr final : public VNVisitor { //in the hierarchy of the model, we can check for this variable. If a variable is found, with //its name added to the current hierarchy, that siuts the target string, an edited version and //the original version are added to the instrumentation config map. - void visit(AstVar* nodep) { + void visit(AstVar* nodep) override { if (m_targetModp != nullptr) { const InstrumentTarget& target = V3Control::getInstrumentCfg().find(m_currHier)->second; for (const auto& entry : target.entries) { if (nodep->name() == entry.varTarget) { - int width; + int width = 0; AstBasicDType* basicp = nodep->basicp(); bool literal = basicp->isLiteralType(); bool implicit = basicp->implicit(); @@ -656,9 +655,9 @@ class InstrumentFunc final : public VNVisitor { } } AstNode* createDPIInterface(AstModule* nodep, AstVar* orig_varp, const string& task_name) { - AstBasicDType* basicp = nullptr; + AstVar* varp = nullptr; if (orig_varp->basicp()->isLiteralType() || orig_varp->basicp()->implicit()) { - int width; + int width = 0; if (orig_varp->basicp()->implicit()) { // Since Var is implicit set/assume the width as 1 like in V3Width.cpp in the // AstVar visitor @@ -667,17 +666,37 @@ class InstrumentFunc final : public VNVisitor { width = orig_varp->basicp()->rangep()->elementsConst(); } if (width <= 1) { - basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT}; + varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT}}; + varp->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + varp->funcReturn(true); + varp->direction(VDirection::OUTPUT); } else if (width <= 8) { - basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BYTE}; + varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BYTE}}; + varp->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + varp->funcReturn(true); + varp->direction(VDirection::OUTPUT); } else if (width <= 16) { - basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::SHORTINT}; + varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::SHORTINT}}; + varp->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + varp->funcReturn(true); + varp->direction(VDirection::OUTPUT); } else if (width <= 32) { - basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT}; + varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT}}; + varp->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + varp->funcReturn(true); + varp->direction(VDirection::OUTPUT); } else if (width <= 64) { - basicp = new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::LONGINT}; + varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::LONGINT}}; + varp->lifetime(VLifetime::AUTOMATIC_IMPLICIT); + varp->funcReturn(true); + varp->direction(VDirection::OUTPUT); } - return new AstFunc{nodep->fileline(), m_task_name, nullptr, basicp}; + return new AstFunc{nodep->fileline(), m_task_name, nullptr, varp}; } else { return new AstTask{nodep->fileline(), m_task_name, nullptr}; } @@ -755,6 +774,7 @@ class InstrumentFunc final : public VNVisitor { m_funcp = VN_CAST(m_dpip, Func); m_funcp->dpiImport(true); m_funcp->prototype(true); + m_funcp->verilogFunction(true); nodep->addStmtsp(m_funcp); } if (VN_IS(m_dpip, Task)) { @@ -770,15 +790,20 @@ class InstrumentFunc final : public VNVisitor { m_tmp_varp->trace(true); } nodep->addStmtsp(m_tmp_varp); - m_dpi_trigger = new AstVar{ - nodep->fileline(), VVarType::VAR, "dpi_trigger", VFlagChildDType{}, - new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT, VSigning::NOSIGN}}; - m_dpi_trigger->trace(false); - nodep->addStmtsp(m_dpi_trigger); - m_loopp = new AstLoop{nodep->fileline()}; - AstInitial* initialp = new AstInitial{ - nodep->fileline(), new AstBegin{nodep->fileline(), "", m_loopp, false}}; - nodep->addStmtsp(initialp); + // Pruefung einbauen ob das schon passiert ist. + if (m_dpi_trigger == nullptr) { + std::cout << "Added dpi_trigger" << std::endl; + m_dpi_trigger = new AstVar{ + nodep->fileline(), VVarType::VAR, "dpi_trigger", VFlagChildDType{}, + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT, + VSigning::NOSIGN}}; + m_dpi_trigger->trace(false); + nodep->addStmtsp(m_dpi_trigger); + m_loopp = new AstLoop{nodep->fileline()}; + AstInitial* initialp = new AstInitial{ + nodep->fileline(), new AstBegin{nodep->fileline(), "", m_loopp, false}}; + nodep->addStmtsp(initialp); + } if (m_taskp != nullptr) { m_taskrefp = new AstTaskRef{ nodep->fileline(), m_task_name, @@ -791,12 +816,12 @@ class InstrumentFunc final : public VNVisitor { } if (m_funcp != nullptr) { m_funcrefp = new AstFuncRef{nodep->fileline(), m_funcp, nullptr}; - m_assignwp - = new AstAssignW{nodep->fileline(), - new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, - m_tmp_varp->name()}, - m_funcrefp}; - nodep->addStmtsp(m_assignwp); + m_assignwp = new AstAssignW{ + nodep->fileline(), new AstParseRef{nodep->fileline(), m_tmp_varp->name()}, + m_funcrefp}; + AstAlways* alwaysp = new AstAlways{nodep->fileline(), VAlwaysKwd::CONT_ASSIGN, + nullptr, m_assignwp}; + nodep->addStmtsp(alwaysp); } if (m_targetIndex == entries.size() - 1) { setDone(nodep); } for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { @@ -817,8 +842,7 @@ class InstrumentFunc final : public VNVisitor { for (AstNode* n = instCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } m_instGenBlock = new AstGenBlock{nodep->fileline(), "", instCellp, false}; AstGenIf* genifp = new AstGenIf{ - nodep->fileline(), - new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, "INSTRUMENT"}, + nodep->fileline(), new AstParseRef{nodep->fileline(), "INSTRUMENT"}, m_instGenBlock, new AstGenBlock{nodep->fileline(), "", getMapEntryCell(m_targetKey)->cloneTree(false), false}}; @@ -838,9 +862,9 @@ class InstrumentFunc final : public VNVisitor { m_addedFunc = false; m_addedport = false; m_instGenBlock = nullptr; - m_dpi_trigger = nullptr; - m_loopp = nullptr; } + m_dpi_trigger = nullptr; + m_loopp = nullptr; m_targetIndex = 0; } @@ -852,7 +876,7 @@ class InstrumentFunc final : public VNVisitor { //instrumented port on the position of the original port in the module and move the original //port to another pin number. This should ensure the linking over the name and the port //position in the module should work. - void visit(AstPort* nodep) { + void visit(AstPort* nodep) override { if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::OUTPUT && nodep->name() == m_orig_varp->name() && !m_addedport) { m_orig_portp = nodep->cloneTree(false); @@ -879,7 +903,7 @@ class InstrumentFunc final : public VNVisitor { //statment deciding between the instrumented and the original cell can be created/used. A third //action is performed if the variable beeing instrumented is an ouput variable. In this case //the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. - void visit(AstCell* nodep) { + void visit(AstCell* nodep) override { if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) && nodep == getMapEntryCell(m_targetKey)) { nodep->modp(getMapEntryInstModule(m_targetKey)); @@ -900,7 +924,7 @@ class InstrumentFunc final : public VNVisitor { //The function is used to change the pin name of the original variable to the instrumented //variable name. This is done to ensure that the pin is correctly linked to the instrumented //variable in the cell. - void visit(AstPin* nodep) { + void visit(AstPin* nodep) override { if (nodep->name() == m_orig_varp->name() && m_orig_varp->direction() == VDirection::INPUT) { iterateChildren(nodep); @@ -911,7 +935,7 @@ class InstrumentFunc final : public VNVisitor { //ASTTASK VISITOR FUNCTION: //The function is used to further specify the task node created at the module visitor. - void visit(AstTask* nodep) { + void visit(AstTask* nodep) override { if (m_addedTask == false && nodep == m_taskp && m_current_module != nullptr) { AstVar* instrID = nullptr; AstVar* var_x_task = nullptr; @@ -938,7 +962,7 @@ class InstrumentFunc final : public VNVisitor { //ASTFUNC VISITOR FUNCITON: //The function is used to further specify the function node created at the module visitor. - void visit(AstFunc* nodep) { + void visit(AstFunc* nodep) override { if (m_addedFunc == false && nodep == m_funcp && m_current_module != nullptr) { AstVar* instrID = nullptr; AstVar* dpi_trigger = nullptr; @@ -968,9 +992,9 @@ class InstrumentFunc final : public VNVisitor { void visit(AstLoop* nodep) override { if (nodep == m_loopp && m_current_module != nullptr) { AstParseRef* initialParseRefrhsp - = new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_dpi_trigger->name()}; + = new AstParseRef{nodep->fileline(), m_dpi_trigger->name()}; AstParseRef* initialParseReflhsp - = new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_dpi_trigger->name()}; + = new AstParseRef{nodep->fileline(), m_dpi_trigger->name()}; AstBegin* initialBeginp = new AstBegin{ nodep->fileline(), "", new AstAssign{nodep->fileline(), initialParseReflhsp, @@ -986,7 +1010,7 @@ class InstrumentFunc final : public VNVisitor { //ASTALWAYS VISITOR FUNCTION: //The function is used to add the task reference node to the always node and further specify //the always node. - void visit(AstAlways* nodep) { + void visit(AstAlways* nodep) override { if (nodep == m_alwaysp && m_current_module != nullptr) { AstBegin* newBegin = nullptr; @@ -999,7 +1023,7 @@ class InstrumentFunc final : public VNVisitor { iterateChildren(nodep); } - void visit(AstVar* nodep) { + void visit(AstVar* nodep) override { if (m_current_module != nullptr && nodep->name() == m_orig_varp->name()) { m_orig_varp_instMod = nodep; } @@ -1007,7 +1031,7 @@ class InstrumentFunc final : public VNVisitor { //ASTTASKREF VISITOR FUNCTION: //The function is used to further specify the task reference node called by the always node. - void visit(AstTaskRef* nodep) { + void visit(AstTaskRef* nodep) override { if (nodep == m_taskrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; constp_id = new AstConst{ @@ -1019,9 +1043,8 @@ class InstrumentFunc final : public VNVisitor { nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); - nodep->addPinsp(new AstArg{ - nodep->fileline(), "", - new AstParseRef{nodep->fileline(), VParseRefExp::PX_TEXT, m_tmp_varp->name()}}); + nodep->addPinsp(new AstArg{nodep->fileline(), "", + new AstParseRef{nodep->fileline(), m_tmp_varp->name()}}); m_orig_varp_instMod = nullptr; } } @@ -1029,7 +1052,7 @@ class InstrumentFunc final : public VNVisitor { //ASTFUNCREF VISITOR FUNCTION: //The function is used to further specify the function reference node called by the assignw //node - void visit(AstFuncRef* nodep) { + void visit(AstFuncRef* nodep) override { if (nodep == m_funcrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; @@ -1054,7 +1077,7 @@ class InstrumentFunc final : public VNVisitor { //Sets the m_assignw flag to true if the current module is not null. //Necessary for the AstParseRef visitor function to determine if the current node is part of an //assignment. - void visit(AstAssignW* nodep) { + void visit(AstAssignW* nodep) override { if (m_current_module != nullptr) { if (nodep != m_assignwp) { m_assignw = true; } iterateChildren(nodep); @@ -1065,10 +1088,10 @@ class InstrumentFunc final : public VNVisitor { // These two function are used to circumvent the instrumentation of ParseRef nodes for // interfaces - void visit(AstDot* nodep) { + void visit(AstDot* nodep) override { if (m_current_module != nullptr) { m_interface = true; } } - void visit(AstReplicate* nodep) { + void visit(AstReplicate* nodep) override { if (m_current_module != nullptr) { m_interface = true; } } @@ -1083,7 +1106,7 @@ class InstrumentFunc final : public VNVisitor { // to //the instrumented variable. This ensures that the instrumented variable is used as the new //input. - void visit(AstParseRef* nodep) { + void visit(AstParseRef* nodep) override { if (m_current_module != nullptr && m_orig_varp != nullptr && nodep->name() == m_orig_varp->name()) { if (m_assignw && !m_interface && m_orig_varp->direction() != VDirection::OUTPUT) { diff --git a/test_regress/t/t_instrumentDPI.cpp b/test_regress/t/t_instrumentDPI.cpp index b5e565435..001f19717 100644 --- a/test_regress/t/t_instrumentDPI.cpp +++ b/test_regress/t/t_instrumentDPI.cpp @@ -20,7 +20,7 @@ extern "C" int instrument_var(int id, int trigger, const svLogic* x) { } //return 0; case 1: - if ((VL_TIME_Q() >= 0 && VL_TIME_Q() < 3) || (VL_TIME_Q() >= 32 && VL_TIME_Q() < 69)) { + if ((VL_TIME_Q() < 3) || (VL_TIME_Q() >= 32 && VL_TIME_Q() < 69)) { return 1; } else { return *x; From b8d1654fb897069cc972e8d1a8f65b18ad001001 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 12 Nov 2025 09:22:21 +0100 Subject: [PATCH 14/20] fix: Enable visting of different assign types --- src/V3Instrument.cpp | 71 +++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 27 deletions(-) diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index c88e0a5bb..1733d0ffc 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -425,9 +425,9 @@ class InstrumentTargetFndr final : public VNVisitor { AstBasicDType* basicp = nodep->basicp(); bool literal = basicp->isLiteralType(); bool implicit = basicp->implicit(); - if (!implicit) { - // Since the basicp is not implicit, there should be a rangep indicating - // the width + if (!implicit && nodep->basicp()->rangep() != nullptr) { + // Since the basicp is not implicit and there is a rangep, we can use the + // rangep for deducting the width width = nodep->basicp()->rangep()->elementsConst(); } bool isUnsupportedType = !literal && !implicit; @@ -491,6 +491,7 @@ public: // Instrumentation class functions class InstrumentFunc final : public VNVisitor { bool m_assignw = false; // Flag if a assignw exists in the netlist + bool m_assignNode = false; // Set to true to indicate that the visitor is in an assign bool m_addedport = false; // Flag if a port was already added bool m_addedTask = false; // Flag if a task was already added bool m_addedFunc = false; // Flag if a function was already added @@ -654,16 +655,42 @@ class InstrumentFunc final : public VNVisitor { if (nodep == pair.second.instrModulep) { pair.second.done = true; } } } + void instrAssigns(AstNodeAssign* nodep) { + if (m_current_module != nullptr && m_orig_varp != nullptr && m_assignwp != nodep) { + m_assignNode = true; + VDirection dir = m_orig_varp->direction(); + if (dir == VDirection::INPUT || dir == VDirection::NONE) { + // Hier muss was mit dem rhsp gemacht werden + AstNodeExpr* rhsp = nodep->rhsp(); + if(rhsp->type() != VNType::ParseRef) { + // Muss ich hier loopen? + for(AstNode* n = rhsp->op1p(); n; n = n->nextp()) { + if(n->type() == VNType::ParseRef && n->name() == m_orig_varp->name()) { + n->name(m_tmp_varp->name()); + break; + } + } + } else { + if(rhsp->name() == m_orig_varp->name()) { + rhsp->name(m_tmp_varp->name()); + } + } + } + } else if (nodep == m_assignwp) { + iterateChildren(nodep); + } + m_assignNode = false; + } AstNode* createDPIInterface(AstModule* nodep, AstVar* orig_varp, const string& task_name) { AstVar* varp = nullptr; if (orig_varp->basicp()->isLiteralType() || orig_varp->basicp()->implicit()) { int width = 0; - if (orig_varp->basicp()->implicit()) { + if (!orig_varp->basicp()->implicit() && orig_varp->basicp()->rangep() != nullptr) { + width = orig_varp->basicp()->rangep()->elementsConst(); + } else { // Since Var is implicit set/assume the width as 1 like in V3Width.cpp in the // AstVar visitor width = 1; - } else { - width = orig_varp->basicp()->rangep()->elementsConst(); } if (width <= 1) { varp = new AstVar{nodep->fileline(), VVarType::VAR, task_name, VFlagChildDType{}, @@ -755,7 +782,6 @@ class InstrumentFunc final : public VNVisitor { m_task_name = getMapEntryFunction(m_targetKey, m_targetIndex); if (isInstModEntry(nodep, m_targetKey) && !isDone(nodep)) { m_current_module = nodep; - for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { if (VN_IS(n, Task) && n->name() == m_task_name) { m_taskp = VN_CAST(n, Task); @@ -792,7 +818,6 @@ class InstrumentFunc final : public VNVisitor { nodep->addStmtsp(m_tmp_varp); // Pruefung einbauen ob das schon passiert ist. if (m_dpi_trigger == nullptr) { - std::cout << "Added dpi_trigger" << std::endl; m_dpi_trigger = new AstVar{ nodep->fileline(), VVarType::VAR, "dpi_trigger", VFlagChildDType{}, new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::BIT, @@ -858,7 +883,6 @@ class InstrumentFunc final : public VNVisitor { m_taskrefp = nullptr; m_addedTask = false; m_funcp = nullptr; - m_funcrefp = nullptr; m_addedFunc = false; m_addedport = false; m_instGenBlock = nullptr; @@ -1070,6 +1094,7 @@ class InstrumentFunc final : public VNVisitor { nodep->addPinsp(new AstArg{nodep->fileline(), "", added_triggerp}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); m_orig_varp_instMod = nullptr; + m_funcrefp = nullptr; } } @@ -1078,21 +1103,16 @@ class InstrumentFunc final : public VNVisitor { //Necessary for the AstParseRef visitor function to determine if the current node is part of an //assignment. void visit(AstAssignW* nodep) override { - if (m_current_module != nullptr) { - if (nodep != m_assignwp) { m_assignw = true; } - iterateChildren(nodep); - } - m_assignw = false; - m_interface = false; + instrAssigns(nodep); } - - // These two function are used to circumvent the instrumentation of ParseRef nodes for - // interfaces - void visit(AstDot* nodep) override { - if (m_current_module != nullptr) { m_interface = true; } + void visit (AstAssign* nodep) override { + instrAssigns(nodep); } - void visit(AstReplicate* nodep) override { - if (m_current_module != nullptr) { m_interface = true; } + void visit(AstAssignDly* nodep) override { + instrAssigns(nodep); + } + void visit(AstAssignForce* nodep) override { + instrAssigns(nodep); } //ASTPARSE REF VISITOR FUNCTION: @@ -1107,11 +1127,8 @@ class InstrumentFunc final : public VNVisitor { //the instrumented variable. This ensures that the instrumented variable is used as the new //input. void visit(AstParseRef* nodep) override { - if (m_current_module != nullptr && m_orig_varp != nullptr - && nodep->name() == m_orig_varp->name()) { - if (m_assignw && !m_interface && m_orig_varp->direction() != VDirection::OUTPUT) { - nodep->name(m_tmp_varp->name()); - } else if (m_orig_varp->direction() == VDirection::INPUT) { + if (m_current_module != nullptr && m_orig_varp != nullptr && m_orig_varp->direction() != VDirection::OUTPUT) { + if (nodep->name() == m_orig_varp->name() && !m_assignNode) { nodep->name(m_tmp_varp->name()); } } From d43e918faf2422add9ea7cca351f64b543917c30 Mon Sep 17 00:00:00 2001 From: github action Date: Tue, 25 Nov 2025 10:47:32 +0000 Subject: [PATCH 15/20] Apply 'make format' --- src/V3Instrument.cpp | 31 +++++++++++-------------------- 1 file changed, 11 insertions(+), 20 deletions(-) diff --git a/src/V3Instrument.cpp b/src/V3Instrument.cpp index 1733d0ffc..7cbe8d30c 100644 --- a/src/V3Instrument.cpp +++ b/src/V3Instrument.cpp @@ -491,7 +491,7 @@ public: // Instrumentation class functions class InstrumentFunc final : public VNVisitor { bool m_assignw = false; // Flag if a assignw exists in the netlist - bool m_assignNode = false; // Set to true to indicate that the visitor is in an assign + bool m_assignNode = false; // Set to true to indicate that the visitor is in an assign bool m_addedport = false; // Flag if a port was already added bool m_addedTask = false; // Flag if a task was already added bool m_addedFunc = false; // Flag if a function was already added @@ -662,18 +662,16 @@ class InstrumentFunc final : public VNVisitor { if (dir == VDirection::INPUT || dir == VDirection::NONE) { // Hier muss was mit dem rhsp gemacht werden AstNodeExpr* rhsp = nodep->rhsp(); - if(rhsp->type() != VNType::ParseRef) { + if (rhsp->type() != VNType::ParseRef) { // Muss ich hier loopen? - for(AstNode* n = rhsp->op1p(); n; n = n->nextp()) { - if(n->type() == VNType::ParseRef && n->name() == m_orig_varp->name()) { + for (AstNode* n = rhsp->op1p(); n; n = n->nextp()) { + if (n->type() == VNType::ParseRef && n->name() == m_orig_varp->name()) { n->name(m_tmp_varp->name()); break; } } } else { - if(rhsp->name() == m_orig_varp->name()) { - rhsp->name(m_tmp_varp->name()); - } + if (rhsp->name() == m_orig_varp->name()) { rhsp->name(m_tmp_varp->name()); } } } } else if (nodep == m_assignwp) { @@ -1102,18 +1100,10 @@ class InstrumentFunc final : public VNVisitor { //Sets the m_assignw flag to true if the current module is not null. //Necessary for the AstParseRef visitor function to determine if the current node is part of an //assignment. - void visit(AstAssignW* nodep) override { - instrAssigns(nodep); - } - void visit (AstAssign* nodep) override { - instrAssigns(nodep); - } - void visit(AstAssignDly* nodep) override { - instrAssigns(nodep); - } - void visit(AstAssignForce* nodep) override { - instrAssigns(nodep); - } + void visit(AstAssignW* nodep) override { instrAssigns(nodep); } + void visit(AstAssign* nodep) override { instrAssigns(nodep); } + void visit(AstAssignDly* nodep) override { instrAssigns(nodep); } + void visit(AstAssignForce* nodep) override { instrAssigns(nodep); } //ASTPARSE REF VISITOR FUNCTION: //The function is used to change the parseref nodes to link to the instrumented variable @@ -1127,7 +1117,8 @@ class InstrumentFunc final : public VNVisitor { //the instrumented variable. This ensures that the instrumented variable is used as the new //input. void visit(AstParseRef* nodep) override { - if (m_current_module != nullptr && m_orig_varp != nullptr && m_orig_varp->direction() != VDirection::OUTPUT) { + if (m_current_module != nullptr && m_orig_varp != nullptr + && m_orig_varp->direction() != VDirection::OUTPUT) { if (nodep->name() == m_orig_varp->name() && !m_assignNode) { nodep->name(m_tmp_varp->name()); } From fab0b69f0b668bd7a44e04c7281d76f524ffef0c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Thu, 27 Nov 2025 10:53:24 +0100 Subject: [PATCH 16/20] style: Changed naming from instrumentation to hook-insertion --- src/CMakeLists.txt | 4 +- src/Makefile_obj.in | 2 +- src/V3Control.cpp | 34 +- src/V3Control.h | 18 +- src/{V3Instrument.cpp => V3InsertHook.cpp} | 456 ++++++++++----------- src/{V3Instrument.h => V3InsertHook.h} | 8 +- src/V3Options.cpp | 2 +- src/V3Options.h | 4 +- src/Verilator.cpp | 10 +- src/verilog.l | 2 +- src/verilog.y | 6 +- 11 files changed, 273 insertions(+), 273 deletions(-) rename src/{V3Instrument.cpp => V3InsertHook.cpp} (76%) rename src/{V3Instrument.h => V3InsertHook.h} (85%) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 5db093c05..fceb8d48d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -117,7 +117,7 @@ set(HEADERS V3Inline.h V3Inst.h V3InstrCount.h - V3Instrument.h + V3InsertHook.h V3Interface.h V3LangCode.h V3LanguageWords.h @@ -290,7 +290,7 @@ set(COMMON_SOURCES V3Inline.cpp V3Inst.cpp V3InstrCount.cpp - V3Instrument.cpp + V3InsertHook.cpp V3Interface.cpp V3Life.cpp V3LifePost.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 5f6704a3b..06df7b87f 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -287,7 +287,7 @@ RAW_OBJS_PCH_ASTNOMT = \ V3Inline.o \ V3Inst.o \ V3InstrCount.o \ - V3Instrument.o \ + V3InsertHook.o \ V3Interface.o \ V3Life.o \ V3LifePost.o \ diff --git a/src/V3Control.cpp b/src/V3Control.cpp index e0fd078db..0fc2c86e6 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -576,7 +576,7 @@ class V3ControlResolver final { uint8_t m_mode = NONE; std::unordered_map m_hierWorkers; FileLine* m_profileFileLine = nullptr; - std::map m_instrCfg; + std::map m_hookInsCfg; V3ControlResolver() = default; ~V3ControlResolver() = default; @@ -641,7 +641,7 @@ public: return cost; } } - // Helper for adding targets to the instrumentation config map + // Helper for adding targets to the hook-insertion config map std::pair splitPrefixAndVar(const string& target) { auto pos = target.rfind('.'); if (pos == string::npos) { @@ -651,13 +651,13 @@ public: string varTarget = target.substr(pos + 1); return {prefix, varTarget}; } - // Add the instrumentation config data to the map to create the initial map (Used in verilog.y) - void addInstrumentCfg(FileLine* fl, const string& instrFunction, int instrID, + // Add the hook-insertion config data to the map to create the initial map (Used in verilog.y) + void addHookInsCfg(FileLine* fl, const string& insFunction, int insID, const string& target) { - // Error MSG if the instrumentation of the top module is not possible + // Error MSG if the hook-insertion of the top module is not possible if ((std::count(target.begin(), target.end(), '.') < 2)) { v3fatal( - "In .vlt defined target tries to instrument the highest MODULE, is not possible!" + "In .vlt defined target tries to insert-hook to the highest MODULE, which is not possible!" " ... Target string: " << target); } @@ -667,19 +667,19 @@ public: auto result = splitPrefixAndVar(target); auto prefix = result.first; auto varTarget = result.second; - InstrumentEntry entry{instrID, instrFunction, varTarget, {}, {}}; - auto it = m_instrCfg.find(prefix); - if (it != m_instrCfg.end()) { + HookInsertEntry entry{insID, insFunction, varTarget, {}, {}}; + auto it = m_hookInsCfg.find(prefix); + if (it != m_hookInsCfg.end()) { it->second.entries.push_back(entry); } else { // Create a new entry in the map - InstrumentTarget newTarget; + HookInsertTarget newTarget; newTarget.entries.push_back(entry); - m_instrCfg[prefix] = std::move(newTarget); + m_hookInsCfg[prefix] = std::move(newTarget); } } - std::map& getInstrumentCfg() { - return m_instrCfg; + std::map& getHookInsCfg() { + return m_hookInsCfg; } }; @@ -739,9 +739,9 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) { V3ControlResolver::s().modules().at(module).addModulePragma(pragma); } -void V3Control::addInstrumentCfg(FileLine* fl, const string& instrumentfunc, int instrID, +void V3Control::addHookInsCfg(FileLine* fl, const string& insfunc, int insID, const string& target) { - V3ControlResolver::s().addInstrumentCfg(fl, instrumentfunc, instrID, target); + V3ControlResolver::s().addHookInsCfg(fl, insfunc, insID, target); } void V3Control::addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost) { @@ -905,8 +905,8 @@ int V3Control::getHierWorkers(const string& model) { FileLine* V3Control::getHierWorkersFileLine(const string& model) { return V3ControlResolver::s().getHierWorkersFileLine(model); } -std::map& V3Control::getInstrumentCfg() { - return V3ControlResolver::s().getInstrumentCfg(); +std::map& V3Control::getHookInsCfg() { + return V3ControlResolver::s().getHookInsCfg(); } uint64_t V3Control::getProfileData(const string& hierDpi) { return V3ControlResolver::s().getProfileData(hierDpi); diff --git a/src/V3Control.h b/src/V3Control.h index e57d764e9..cc93ff468 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -33,18 +33,18 @@ struct LengthThenLexiographic final { return a < b; } }; -struct InstrumentEntry final { - int instrID; - std::string instrFunc; +struct HookInsertEntry final { + int insID; + std::string insFunc; std::string varTarget; AstVar* origVarps; - AstVar* instrVarps; + AstVar* insVarps; bool found = false; }; -struct InstrumentTarget final { - std::vector entries; +struct HookInsertTarget final { + std::vector entries; AstModule* origModulep; - AstModule* instrModulep; + AstModule* insModulep; AstModule* topModulep; AstModule* pointingModulep; AstCell* cellp; @@ -70,9 +70,9 @@ public: static void addIgnoreMatch(V3ErrorCode code, const string& filename, const string& contents, const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); - static void addInstrumentCfg(FileLine* fl, const string& instrumentfunc, int instrID, + static void addHookInsCfg(FileLine* fl, const string& insfunc, int insID, const string& target); - static std::map& getInstrumentCfg(); + static std::map& getHookInsCfg(); static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); static void addProfileData(FileLine* fl, const string& model, const string& key, diff --git a/src/V3Instrument.cpp b/src/V3InsertHook.cpp similarity index 76% rename from src/V3Instrument.cpp rename to src/V3InsertHook.cpp index 7cbe8d30c..c027ea13c 100644 --- a/src/V3Instrument.cpp +++ b/src/V3InsertHook.cpp @@ -1,10 +1,10 @@ // -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* +//************************************************************************** // DESCRIPTION: Verilator: // // 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 @@ -13,18 +13,18 @@ // SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 // //************************************************************************* -// V3Instrumentation's Transformations: -// The instrumentation configuration map is populated with the relevant nodes, as defined by the -// target string specified in the instrumentation configuration within the .vlt file. +// V3HookInsert's Transformations: +// The hook-insertion configuration map is populated with the relevant nodes, as defined by the +// target string specified in the hook-insertion configuration within the .vlt file. // Additionally, the AST (Abstract Syntax Tree) is modified to insert the necessary extra nodes -// required for instrumentation. +// required for hook-insertion. // Furthermore, the links between Module, Cell, and Var nodes are adjusted to ensure correct -// connectivity for instrumentation purposes. +// connectivity for hook-insertion purposes. //************************************************************************* #include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT -#include "V3Instrument.h" +#include "V3InsertHook.h" #include "V3Control.h" #include "V3File.h" @@ -39,8 +39,8 @@ VL_DEFINE_DEBUG_FUNCTIONS; //################################################################################## -// Instrumentation class finder -class InstrumentTargetFndr final : public VNVisitor { +// Hook-insertion class finder +class HookInsTargetFndr final : public VNVisitor { AstNetlist* m_netlist = nullptr; // Enable traversing from the beginning if the visitor is to deep AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node @@ -51,7 +51,7 @@ class InstrumentTargetFndr final : public VNVisitor { bool m_foundModp = false; // If the visitor found the relevant model bool m_foundVarp = false; // If the visitor found the relevant variable bool m_initModp = true; // If the visitor is in the first module node of the netlist - size_t m_instrIdx = 0; + size_t m_insIdx = 0; string m_currHier; // Stores the current hierarchy of the visited nodes (Module, Cell, Var) string m_target; // Stores the currently visited target string from the config map @@ -74,31 +74,31 @@ class InstrumentTargetFndr final : public VNVisitor { // Helper function to check if a parameter was already added to the tree previously bool hasParam(AstModule* modp) { for (AstNode* n = modp->op2p(); n; n = n->nextp()) { - if (n->name() == "INSTRUMENT") { return true; } + if (n->name() == "HOOKINS") { return true; } } return false; } // Helper function to check if a pin was already added to the tree previously bool hasPin(AstCell* cellp) { for (AstNode* n = cellp->paramsp(); n; n = n->nextp()) { - if (n->name() == "INSTRUMENT") { return true; } + if (n->name() == "HOOKINS") { return true; } } return false; } // Check if the multipleCellps flag is set for the given target bool hasMultiple(const std::string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { return it->second.multipleCellps; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { return it->second.multipleCellps; } return false; } - // Check if the direct predecessor in the target string has been instrumented, - // to create the correct link between the already instrumented module and the current one. + // Check if the direct predecessor in the target string has been hook-inserted, + // to create the correct link between the already hook-inserted module and the current one. bool hasPrior(AstModule* modulep, const string& target) { - const auto& instrCfg = V3Control::getInstrumentCfg(); + const auto& insCfg = V3Control::getHookInsCfg(); auto priorTarget = reduce2Depth(split(target), KeyDepth::RelevantModule); - auto it = instrCfg.find(priorTarget); - return it != instrCfg.end() && it->second.processed; + auto it = insCfg.find(priorTarget); + return it != insCfg.end() && it->second.processed; } bool targetHasFullName(const string& fullname, const string& target) { return fullname == target; @@ -137,7 +137,7 @@ class InstrumentTargetFndr final : public VNVisitor { } // Helper function for adding the parameters into the tree void addParam(AstModule* modp) { - AstVar* paramp = new AstVar{modp->fileline(), VVarType::GPARAM, "INSTRUMENT", + AstVar* paramp = new AstVar{modp->fileline(), VVarType::GPARAM, "HOOKINS", VFlagChildDType{}, nullptr}; paramp->valuep(new AstConst{modp->fileline(), AstConst::Signed32{}, 0}); paramp->dtypep(paramp->valuep()->dtypep()); @@ -145,34 +145,34 @@ class InstrumentTargetFndr final : public VNVisitor { modp->addStmtsp(paramp); } // Helper function for adding the parameters into the tree - void addPin(AstCell* cellp, bool isInstrumentPath) { + void addPin(AstCell* cellp, bool isInsPath) { int pinnum = 0; - if (isInstrumentPath) { + if (isInsPath) { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "INSTRUMENT", - // The pin is set to 1 to enable the instrumentation path + AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "HOOKINS", + // The pin is set to 1 to enable the hook-insertion path new AstConst{cellp->fileline(), AstConst::Signed32{}, 1}}; pinp->param(true); cellp->addParamsp(pinp); } else { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } - AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "INSTRUMENT", - new AstParseRef{cellp->fileline(), "INSTRUMENT"}}; + AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "HOOKINS", + new AstParseRef{cellp->fileline(), "HOOKINS"}}; pinp->param(true); cellp->addParamsp(pinp); } } - // Edit the instrumentation data for the cell in the map - void editInstrData(AstCell* cellp, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.cellp = cellp; } + // Edit the hook-insertion data for the cell in the map + void editInsData(AstCell* cellp, const string& target) { + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.cellp = cellp; } } - // Edit the instrumentation data for the pointing module in the map - void editInstrData(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } + // Edit the hook-insertion data for the pointing module in the map + void editInsData(AstModule* modulep, const string& target) { + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.pointingModulep = modulep; } } // Check for multiple cells pointing to the next module void multCellForModp(AstCell* cellp) { @@ -189,53 +189,53 @@ class InstrumentTargetFndr final : public VNVisitor { } // Insert the cell node that is/will pointing/point to the targeted module void setCell(AstCell* cellp, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.cellp = cellp; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.cellp = cellp; } } - // Insert the original and instrumented module nodes to the map - void setInstrModule(AstModule* origModulep, AstModule* instrModulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { + // Insert the original and hook-inserted module nodes to the map + void setInsModule(AstModule* origModulep, AstModule* insModulep, const string& target) { + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.origModulep = origModulep; - it->second.instrModulep = instrModulep; + it->second.insModulep = insModulep; } } // Set the multipleCellps flag void setMultiple(const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.multipleCellps = true; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.multipleCellps = true; } } // Insert the module node that includes the cell pointing to the targeted module // to the map void setPointingMod(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.pointingModulep = modulep; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.pointingModulep = modulep; } } // Set the processed flag void setProcessed(const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.processed = true; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.processed = true; } } // Insert the top module node of the netlist to the map void setTopMod(AstModule* modulep, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { it->second.topModulep = modulep; } + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { it->second.topModulep = modulep; } } - // Insert the original and instrumented variable nodes to the map - void setVar(AstVar* varp, AstVar* instVarp, const string& target) { - auto& instrCfg = V3Control::getInstrumentCfg(); - auto it = instrCfg.find(target); - if (it != instrCfg.end()) { + // Insert the original and hook-inserted variable nodes to the map + void setVar(AstVar* varp, AstVar* insVarp, const string& target) { + auto& insCfg = V3Control::getHookInsCfg(); + auto it = insCfg.find(target); + if (it != insCfg.end()) { for (auto& entry : it->second.entries) { if (entry.varTarget == varp->name()) { entry.origVarps = varp; - entry.instrVarps = instVarp; + entry.insVarps = insVarp; entry.found = true; return; } @@ -250,9 +250,9 @@ class InstrumentTargetFndr final : public VNVisitor { //Iterates over the existing module nodes in the netlist. //For the first module in the netlist the node name is checked if it is at the first position //in the target string provided by the configuration file. If not an error is thown, otherwise - //the modules is checked for an already existing INSTRUMENT parameter. If there is no - //INSTRUMENT parameter present we add it to the module. This parameter is used to control the - //instrumentation of the target. The module is then added to the map of the instrumentation + //the modules is checked for an already existing HOOKINS parameter. If there is no + //HOOKINS parameter present we add it to the module. This parameter is used to control the + //hook-insertion of the target. The module is then added to the map of the hook-insertion //configs as the top module. Additionally the hierarchy the function viewed is currently add is //initialized with the module name. This module hierarchy is used to identify the correct //target path in the netlist. The function iterates over the children of the module, with the @@ -265,9 +265,9 @@ class InstrumentTargetFndr final : public VNVisitor { //The module node displayed by the m_modp variable is then checked if this is the module //containing the target variable (relevant module) or if it the module containing the cell //pointing to the relevant module (pointing module). If the module node suits one of these two - //conditions the module nodes are added to the instrumentation configs map. Independetly from - //these conditions the INSTRUMENT parameter is added to the module nodes in the target path. - //This parameter is used to control the instrumentation of the target. + //conditions the module nodes are added to the hook-insertion configs map. Independetly from + //these conditions the HOOKINS parameter is added to the module nodes in the target path. + //This parameter is used to control the hook-insertion of the target. void visit(AstModule* nodep) override { if (m_initModp) { if (targetHasTop(nodep->name(), m_target)) { @@ -293,32 +293,32 @@ class InstrumentTargetFndr final : public VNVisitor { } else if (m_cellModp != nullptr && (nodep = findModp(m_netlist, VN_CAST(m_cellModp, Module))) != nullptr) { if (targetHasFullName(m_currHier, m_target)) { - AstModule* instrModp = nullptr; + AstModule* insModp = nullptr; m_foundModp = true; m_targetModp = nodep; m_cellModp = nullptr; // Check for prior changes made to the tree if (hasPrior(nodep, m_currHier)) { - auto& instrCfg = V3Control::getInstrumentCfg(); - instrModp - = instrCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule)) - ->second.instrModulep; - editInstrData(instrModp, m_currHier); + auto& insCfg = V3Control::getHookInsCfg(); + insModp + = insCfg.find(reduce2Depth(split(m_currHier), KeyDepth::RelevantModule)) + ->second.insModulep; + editInsData(insModp, m_currHier); AstCell* cellp = nullptr; - for (AstNode* n = instrModp->op2p(); n; n = n->nextp()) { + for (AstNode* n = insModp->op2p(); n; n = n->nextp()) { if (VN_IS(n, Cell) && (VN_CAST(n, Cell)->modp() == nodep) - && instrCfg.find(m_currHier)->second.cellp->name() == n->name()) { + && insCfg.find(m_currHier)->second.cellp->name() == n->name()) { cellp = VN_CAST(n, Cell); break; } } - editInstrData(cellp, m_currHier); + editInsData(cellp, m_currHier); } if (!hasParam(nodep)) { addParam(nodep); } - instrModp = nodep->cloneTree(false); - instrModp->name(nodep->name() + "__inst__" + std::to_string(m_instrIdx)); - if (hasMultiple(m_target)) { instrModp->inLibrary(true); } - setInstrModule(nodep, instrModp, m_target); + insModp = nodep->cloneTree(false); + insModp->name(nodep->name() + "__hookIns__" + std::to_string(m_insIdx)); + if (hasMultiple(m_target)) { insModp->inLibrary(true); } + setInsModule(nodep, insModp, m_target); iterateChildren(nodep); } else if (targetHasPointingMod(m_currHier, m_target)) { m_foundModp = true; @@ -356,14 +356,14 @@ class InstrumentTargetFndr final : public VNVisitor { //false. The in the current module existing cells are checked if there are multiple cells //linking to the next module in the target string. After that the m_modp is updated to match //the cell's module pointer, which is needed for the next call of the module visitor. Next the - //pin for the INSTRUMENT parameter is added to the cell. This parameter is added either as a + //pin for the HOOKINS parameter is added to the cell. This parameter is added either as a //constant or as a reference, depending on the traversal stage. If there are multiple cells //linking to the next module in the target string, the multiple flag is set in the - //instrumentation config map. For the inistial module the found cell is then added to the - //instrumentation configuration map with the current hierarchy as the target path. Otherwise - //the cell is added to the instrumentation configuration map, when the current hierarchy with + //hook-insertion config map. For the inistial module the found cell is then added to the + //hook-insertion configuration map with the current hierarchy as the target path. Otherwise + //the cell is added to the hook-insertion configuration map, when the current hierarchy with //the cell name fully matches a target path, with the last two entrances removed (Module, Var). - //This function ensures that the correct cells in the design hierarchy are instrumented and + //This function ensures that the correct cells in the design hierarchy are hook-inserted and //tracked, supporting both unique and repeated module instances. void visit(AstCell* nodep) override { if (m_initModp) { @@ -414,11 +414,11 @@ class InstrumentTargetFndr final : public VNVisitor { //module of the target hierarchy. Since we therefore know that we will not traverse any further //in the hierarchy of the model, we can check for this variable. If a variable is found, with //its name added to the current hierarchy, that siuts the target string, an edited version and - //the original version are added to the instrumentation config map. + //the original version are added to the hook-insertion config map. void visit(AstVar* nodep) override { if (m_targetModp != nullptr) { - const InstrumentTarget& target - = V3Control::getInstrumentCfg().find(m_currHier)->second; + const HookInsertTarget& target + = V3Control::getHookInsCfg().find(m_currHier)->second; for (const auto& entry : target.entries) { if (nodep->name() == entry.varTarget) { int width = 0; @@ -446,8 +446,8 @@ class InstrumentTargetFndr final : public VNVisitor { setVar(nodep, varp, m_target); if (string::npos == m_currHier.rfind('.')) { AstModule* modulep = m_modp->cloneTree(false); - modulep->name(m_modp->name() + "__inst__" + std::to_string(m_instrIdx)); - setInstrModule(m_modp, modulep, m_currHier); + modulep->name(m_modp->name() + "__hookIns__" + std::to_string(m_insIdx)); + setInsModule(m_modp, modulep, m_currHier); m_initModp = false; } m_foundVarp = true; @@ -466,9 +466,9 @@ class InstrumentTargetFndr final : public VNVisitor { public: // CONSTRUCTOR //------------------------------------------------------------------------------- - explicit InstrumentTargetFndr(AstNetlist* nodep) { - const auto& instrCfg = V3Control::getInstrumentCfg(); - for (const auto& pair : instrCfg) { + explicit HookInsTargetFndr(AstNetlist* nodep) { + const auto& insCfg = V3Control::getHookInsCfg(); + for (const auto& pair : insCfg) { m_netlist = nodep; m_target = pair.first; m_initModp = true; @@ -481,15 +481,15 @@ public: m_error = false; m_targetModp = nullptr; m_modp = nullptr; - m_instrIdx++; + m_insIdx++; } }; - ~InstrumentTargetFndr() override = default; + ~HookInsTargetFndr() override = default; }; //################################################################################## -// Instrumentation class functions -class InstrumentFunc final : public VNVisitor { +// Hook-insertion class functions +class HookInsFunc final : public VNVisitor { bool m_assignw = false; // Flag if a assignw exists in the netlist bool m_assignNode = false; // Set to true to indicate that the visitor is in an assign bool m_addedport = false; // Flag if a port was already added @@ -497,13 +497,13 @@ class InstrumentFunc final : public VNVisitor { bool m_addedFunc = false; // Flag if a function was already added bool m_interface = false; // Flag if the ParseRef node is part of an interface int m_pinnum = 0; // Pinnumber for the new Port nodes - string m_targetKey; // Stores the target string from the instrumentation config + string m_targetKey; // Stores the target string from the hook-insertion config string m_task_name; - size_t m_targetIndex = 0; // Index of the target variable in the instrumentation config + size_t m_targetIndex = 0; // Index of the target variable in the hook-insertion config AstAlways* m_alwaysp = nullptr; // Stores the added always node AstAssignW* m_assignwp = nullptr; // Stores the added assignw node - AstGenBlock* m_instGenBlock - = nullptr; // Store the GenBlock node for instrumentation hierarchy check + AstGenBlock* m_insGenBlock + = nullptr; // Store the GenBlock node for hook-insertion hierarchy check AstTask* m_taskp = nullptr; // // Stores the created task node AstFunc* m_funcp = nullptr; // Stores the created function node AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node @@ -512,10 +512,10 @@ class InstrumentFunc final : public VNVisitor { AstModule* m_current_module = nullptr; // Stores the currenty visited module AstModule* m_current_module_cell_check = nullptr; // Stores the module node(used by cell visitor) - AstVar* m_tmp_varp = nullptr; // Stores the instrumented variable node + AstVar* m_tmp_varp = nullptr; // Stores the hook-inserted variable node AstVar* m_orig_varp = nullptr; // Stores the original variable node - AstVar* m_orig_varp_instMod - = nullptr; // Stores the original variable node in instrumented module node + AstVar* m_orig_varp_insMod + = nullptr; // Stores the original variable node in hook-inserted module node AstVar* m_dpi_trigger = nullptr; // Stores the variable noded for the dpi-trigger, which ensures the changing of // a signal and the execution of the DPI function @@ -523,54 +523,54 @@ class InstrumentFunc final : public VNVisitor { // METHODS //---------------------------------------------------------------------------------- - // Find the relevant instrumentation config in the map corresponding to the given key - const InstrumentTarget* getInstrCfg(const std::string& key) { - const auto& map = V3Control::getInstrumentCfg(); - auto instrCfg = map.find(key); - if (instrCfg != map.end()) { - return &instrCfg->second; + // Find the relevant hook-insertion config in the map corresponding to the given key + const HookInsertTarget* getInsCfg(const std::string& key) { + const auto& map = V3Control::getHookInsCfg(); + auto insCfg = map.find(key); + if (insCfg != map.end()) { + return &insCfg->second; } else { return nullptr; } } // Get the Cell nodep pointer from the configuration map for the given key AstCell* getMapEntryCell(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { return cfg->cellp; } + if (auto cfg = getInsCfg(key)) { return cfg->cellp; } return nullptr; } - // Get the instrumented Module node pointer from the configuration map for the given key - AstModule* getMapEntryInstModule(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { return cfg->instrModulep; } + // Get the hook-inserted Module node pointer from the configuration map for the given key + AstModule* getMapEntryInsModule(const std::string& key) { + if (auto cfg = getInsCfg(key)) { return cfg->insModulep; } return nullptr; } - // Get the Module node pointer pointing to the instrumented/original module from the + // Get the Module node pointer pointing to the hook-inserted/original module from the // configuration map for the given key AstModule* getMapEntryPointingModule(const std::string& key) { - if (auto cfg = getInstrCfg(key)) { return cfg->pointingModulep; } + if (auto cfg = getInsCfg(key)) { return cfg->pointingModulep; } return nullptr; } - // Get the instrumented variable node pointer from the configuration map for the given key - AstVar* getMapEntryInstVar(const std::string& key, size_t index) { - if (auto cfg = getInstrCfg(key)) { + // Get the hook-inserted variable node pointer from the configuration map for the given key + AstVar* getMapEntryInsVar(const std::string& key, size_t index) { + if (auto cfg = getInsCfg(key)) { const auto& entries = cfg->entries; - if (index < entries.size()) { return entries[index].instrVarps; } + if (index < entries.size()) { return entries[index].insVarps; } } return nullptr; } // Get the original variable node pointer from the configuration map for the given key AstVar* getMapEntryVar(const std::string& key, size_t index) { - if (auto cfg = getInstrCfg(key)) { + if (auto cfg = getInsCfg(key)) { const auto& entries = cfg->entries; if (index < entries.size()) { return entries[index].origVarps; } } return nullptr; } - // Check if the given module node pointer is an instrumented module entry in the configuration + // Check if the given module node pointer is an hook-inserted module entry in the configuration // map for the given key - bool isInstModEntry(AstModule* nodep, const std::string& key) { - const auto& map = V3Control::getInstrumentCfg(); - const auto instrCfg = map.find(key); - if (instrCfg != map.end() && instrCfg->second.instrModulep == nodep) { + bool isInsModEntry(AstModule* nodep, const std::string& key) { + const auto& map = V3Control::getHookInsCfg(); + const auto insCfg = map.find(key); + if (insCfg != map.end() && insCfg->second.insModulep == nodep) { return true; } else { return false; @@ -578,47 +578,47 @@ class InstrumentFunc final : public VNVisitor { } // Check if the given module node pointer is the top module entry in the configuration map bool isTopModEntry(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentCfg(); - for (const auto& pair : instrCfg) { + auto& insCfg = V3Control::getHookInsCfg(); + for (const auto& pair : insCfg) { if (nodep == pair.second.topModulep) { return true; } } return false; } // Check if the given module node pointer is the pointing module entry in the configuration map bool isPointingModEntry(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentCfg(); - for (const auto& pair : instrCfg) { + auto& insCfg = V3Control::getHookInsCfg(); + for (const auto& pair : insCfg) { if (nodep == pair.second.pointingModulep) { return true; } } return false; } - // Check if the given module node pointer has already been instrumented/done flag has been set + // Check if the given module node pointer has already been hook-inserted/done flag has been set bool isDone(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentCfg(); - for (const auto& pair : instrCfg) { - if (nodep == pair.second.instrModulep) { return pair.second.done; } + auto& insCfg = V3Control::getHookInsCfg(); + for (const auto& pair : insCfg) { + if (nodep == pair.second.insModulep) { return pair.second.done; } } return true; } // Check if the multipleCellps flag is set for the given key in the configuration map bool hasMultiple(const std::string& key) { - const auto& map = V3Control::getInstrumentCfg(); - const auto instrCfg = map.find(key); - if (instrCfg != map.end()) { - return instrCfg->second.multipleCellps; + const auto& map = V3Control::getHookInsCfg(); + const auto insCfg = map.find(key); + if (insCfg != map.end()) { + return insCfg->second.multipleCellps; } else { return false; } } // Check if the module and instances defined in the target string were found in // the previous step - bool hasNullptr(const std::pair& pair) { + bool hasNullptr(const std::pair& pair) { bool moduleNullptr = pair.second.origModulep == nullptr; bool cellNullptr = pair.second.cellp == nullptr; return moduleNullptr || cellNullptr; } // Check if the, in the target string, defined variable was found in the previous step - bool isFound(const std::pair& pair) { + bool isFound(const std::pair& pair) { for (auto& entry : pair.second.entries) { if (entry.found == false) { return entry.found; } } @@ -626,23 +626,23 @@ class InstrumentFunc final : public VNVisitor { } // Get the fault case for the given key in the configuration map int getMapEntryFaultCase(const std::string& key, size_t index) { - const auto& map = V3Control::getInstrumentCfg(); - const auto instrCfg = map.find(key); - if (instrCfg != map.end()) { - const auto& entries = instrCfg->second.entries; - if (index < entries.size()) { return entries[index].instrID; } + const auto& map = V3Control::getHookInsCfg(); + const auto insCfg = map.find(key); + if (insCfg != map.end()) { + const auto& entries = insCfg->second.entries; + if (index < entries.size()) { return entries[index].insID; } return -1; // Return -1 if index is out of bounds } else { return -1; } } - // Get the instrumentation function name for the given key in the configuration map + // Get the callback function name from the hook-insertion for the given key in the configuration map string getMapEntryFunction(const std::string& key, size_t index) { - const auto& map = V3Control::getInstrumentCfg(); - const auto instrCfg = map.find(key); - if (instrCfg != map.end()) { - const auto& entries = instrCfg->second.entries; - if (index < entries.size()) { return entries[index].instrFunc; } + const auto& map = V3Control::getHookInsCfg(); + const auto insCfg = map.find(key); + if (insCfg != map.end()) { + const auto& entries = insCfg->second.entries; + if (index < entries.size()) { return entries[index].insFunc; } return ""; } else { return ""; @@ -650,12 +650,12 @@ class InstrumentFunc final : public VNVisitor { } // Set the done flag for the given module node pointer in the configuraiton map void setDone(AstModule* nodep) { - auto& instrCfg = V3Control::getInstrumentCfg(); - for (auto& pair : instrCfg) { - if (nodep == pair.second.instrModulep) { pair.second.done = true; } + auto& insCfg = V3Control::getHookInsCfg(); + for (auto& pair : insCfg) { + if (nodep == pair.second.insModulep) { pair.second.done = true; } } } - void instrAssigns(AstNodeAssign* nodep) { + void insAssigns(AstNodeAssign* nodep) { if (m_current_module != nullptr && m_orig_varp != nullptr && m_assignwp != nodep) { m_assignNode = true; VDirection dir = m_orig_varp->direction(); @@ -733,16 +733,16 @@ class InstrumentFunc final : public VNVisitor { //ASTNETLIST VISITOR FUNCTION: //Loop over map entries for module nodes and add them to the tree void visit(AstNetlist* nodep) override { - const auto& instrCfg = V3Control::getInstrumentCfg(); - for (const auto& pair : instrCfg) { + const auto& insCfg = V3Control::getHookInsCfg(); + for (const auto& pair : insCfg) { if (hasNullptr(pair) || !isFound(pair)) { v3error( - "Verilator-configfile: Incomplete instrumentation configuration for target '" + "Verilator-configfile: Incomplete hook-insertion configuration for target '" << pair.first << "'. Please check previous Errors from V3Instrument:findTargets and ensure" << " all necessary components are correct defined."); } else { - nodep->addModulesp(pair.second.instrModulep); + nodep->addModulesp(pair.second.insModulep); m_targetKey = pair.first; iterateChildren(nodep); m_assignw = false; @@ -752,33 +752,33 @@ class InstrumentFunc final : public VNVisitor { //ASTMODULE VISITOR FUNCTION: //This function is called for each module node in the netlist. - //It checks if the module node is part of the instrumentation configuratio map. - //Depending on the type of the module node (Instrumented, Top, Pointing, or Original), + //It checks if the module node is part of the hook-insertion configuration map. + //Depending on the type of the module node (Hook-inserted, Top, Pointing, or Original), //it performs different actions: - // - If the module is an instrumented module entry and has not been done, it creates a new - //task for the instrumentation function, adds the temporary variable, and creates a task - //reference to the instrumentation function. + // - If the module is an hook-inserted module entry and has not been done, it creates a new + //task for the hook-insertion function, adds the temporary variable, and creates a task + //reference to the callback function. // - If the module is a pointing module or a top module and has no multiple cellps, it // checks //the cell for the target key and counts the pins. This pin count is used in the CELL VISITOR - //FUNCTION to set a siutable pin number for the INSTRUMENT parameter. Look there fore further + //FUNCTION to set a siutable pin number for the HOOKINS parameter. Look there fore further //information. // - If the module is a pointing module and has multiple cellps, it creates a begin block // with - //a conditional statement to select between the instrumented and original cell. + //a conditional statement to select between the hook-inserted and original cell. // Additionally like in the previous case, the pin count is used to set a suitable pin - //number for the INSTRUMENT parameter.\ Since the cell which need to be edited are located not + //number for the HOOKINS parameter.\ Since the cell which need to be edited are located not //in the original module, but in the pointing/top module, the current_module_cell_check //variable is set to the module visited by the function and fulfilling this condition. void visit(AstModule* nodep) override { - const auto& instrCfg = V3Control::getInstrumentCfg().find(m_targetKey); - const InstrumentTarget& target = instrCfg->second; + const auto& insCfg = V3Control::getHookInsCfg().find(m_targetKey); + const HookInsertTarget& target = insCfg->second; const auto& entries = target.entries; for (m_targetIndex = 0; m_targetIndex < entries.size(); ++m_targetIndex) { - m_tmp_varp = getMapEntryInstVar(m_targetKey, m_targetIndex); + m_tmp_varp = getMapEntryInsVar(m_targetKey, m_targetIndex); m_orig_varp = getMapEntryVar(m_targetKey, m_targetIndex); m_task_name = getMapEntryFunction(m_targetKey, m_targetIndex); - if (isInstModEntry(nodep, m_targetKey) && !isDone(nodep)) { + if (isInsModEntry(nodep, m_targetKey) && !isDone(nodep)) { m_current_module = nodep; for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { if (VN_IS(n, Task) && n->name() == m_task_name) { @@ -855,23 +855,23 @@ class InstrumentFunc final : public VNVisitor { && (isPointingModEntry(nodep) || isTopModEntry(nodep)) && !hasMultiple(m_targetKey)) { m_current_module_cell_check = nodep; - AstCell* instCellp = getMapEntryCell(m_targetKey); - for (AstNode* n = instCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + AstCell* insCellp = getMapEntryCell(m_targetKey); + for (AstNode* n = insCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } iterateChildren(nodep); } else if (isPointingModEntry(nodep) && hasMultiple(m_targetKey)) { m_current_module_cell_check = nodep; - AstCell* instCellp = getMapEntryCell(m_targetKey)->cloneTree(false); - instCellp->modp(getMapEntryInstModule(m_targetKey)); - for (AstNode* n = instCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } - m_instGenBlock = new AstGenBlock{nodep->fileline(), "", instCellp, false}; + AstCell* insCellp = getMapEntryCell(m_targetKey)->cloneTree(false); + insCellp->modp(getMapEntryInsModule(m_targetKey)); + for (AstNode* n = insCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + m_insGenBlock = new AstGenBlock{nodep->fileline(), "", insCellp, false}; AstGenIf* genifp = new AstGenIf{ - nodep->fileline(), new AstParseRef{nodep->fileline(), "INSTRUMENT"}, - m_instGenBlock, + nodep->fileline(), new AstParseRef{nodep->fileline(), "HOOKINS"}, + m_insGenBlock, new AstGenBlock{nodep->fileline(), "", getMapEntryCell(m_targetKey)->cloneTree(false), false}}; nodep->addStmtsp(genifp); - iterateChildren(m_instGenBlock); + iterateChildren(m_insGenBlock); iterateChildren(nodep); } m_current_module = nullptr; @@ -883,7 +883,7 @@ class InstrumentFunc final : public VNVisitor { m_funcp = nullptr; m_addedFunc = false; m_addedport = false; - m_instGenBlock = nullptr; + m_insGenBlock = nullptr; } m_dpi_trigger = nullptr; m_loopp = nullptr; @@ -893,9 +893,9 @@ class InstrumentFunc final : public VNVisitor { //ASTPORT VISITOR FUNCTION: //When the target variable is an ouput port, this function is called. //If no port is added yet, two new ports are added to the current module. - //This enabled the instrumentation of the ouput port and link this instrumented port to the + //This enabled the hook-insertion of the ouput port and link this hook-inserted port to the //modules reading from the original port. The idea behind this function is to set the - //instrumented port on the position of the original port in the module and move the original + //hook-inserted port on the position of the original port in the module and move the original //port to another pin number. This should ensure the linking over the name and the port //position in the module should work. void visit(AstPort* nodep) override { @@ -913,28 +913,28 @@ class InstrumentFunc final : public VNVisitor { } //ASTCELL VISITOR FUNCTION: - //This function visits the cell nodes in the module pointing to the instrumented module. + //This function visits the cell nodes in the module pointing to the hook-inserted module. //Depending if hasMultiple is set for the target key, two different actions are performed: - // - If hasMultiple is false, the cell is modified to link to the instrumented module and + // - If hasMultiple is false, the cell is modified to link to the hook-inserted module and // the - //children are iterated. This ensures that the instrumented mopdule is used in the cell. Also + //children are iterated. This ensures that the hook-inserted mopdule is used in the cell. Also //if the original variable is an output variable, the children of this cell nodes are visited //by the ASTPIN VISITOR FUNCTION. // - If hasMultiple is true, the cell is unlinked from the back and deleted. // This ensures that the cell is not used anymore in the module, and the conditional - //statment deciding between the instrumented and the original cell can be created/used. A third - //action is performed if the variable beeing instrumented is an ouput variable. In this case + //statment deciding between the hook-inserted and the original cell can be created/used. A third + //action is performed if the variable beeing hook-inserted is an ouput variable. In this case //the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. void visit(AstCell* nodep) override { if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) && nodep == getMapEntryCell(m_targetKey)) { - nodep->modp(getMapEntryInstModule(m_targetKey)); + nodep->modp(getMapEntryInsModule(m_targetKey)); if (m_orig_varp->direction() == VDirection::OUTPUT) { iterateChildren(nodep); } } else if (m_current_module_cell_check != nullptr && hasMultiple(m_targetKey) && nodep == getMapEntryCell(m_targetKey)) { nodep->unlinkFrBack(); nodep->deleteTree(); - } else if (m_instGenBlock != nullptr && nodep->modp() == getMapEntryInstModule(m_targetKey) + } else if (m_insGenBlock != nullptr && nodep->modp() == getMapEntryInsModule(m_targetKey) && m_orig_varp->direction() == VDirection::OUTPUT) { iterateChildren(nodep); } else if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::INPUT) { @@ -943,8 +943,8 @@ class InstrumentFunc final : public VNVisitor { } //ASTPIN VISITOR FUNCTION: - //The function is used to change the pin name of the original variable to the instrumented - //variable name. This is done to ensure that the pin is correctly linked to the instrumented + //The function is used to change the pin name of the original variable to the hook-inserted + //variable name. This is done to ensure that the pin is correctly linked to the hook-inserted //variable in the cell. void visit(AstPin* nodep) override { if (nodep->name() == m_orig_varp->name() @@ -959,14 +959,14 @@ class InstrumentFunc final : public VNVisitor { //The function is used to further specify the task node created at the module visitor. void visit(AstTask* nodep) override { if (m_addedTask == false && nodep == m_taskp && m_current_module != nullptr) { - AstVar* instrID = nullptr; + AstVar* insID = nullptr; AstVar* var_x_task = nullptr; AstVar* tmp_var_task = nullptr; - instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, + insID = new AstVar{nodep->fileline(), VVarType::PORT, "insID", VFlagChildDType{}, new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, VSigning::SIGNED, 32, 0}}; - instrID->direction(VDirection::INPUT); + insID->direction(VDirection::INPUT); var_x_task = m_orig_varp->cloneTree(false); var_x_task->varType(VVarType::PORT); @@ -976,7 +976,7 @@ class InstrumentFunc final : public VNVisitor { tmp_var_task->varType(VVarType::PORT); tmp_var_task->direction(VDirection::OUTPUT); - nodep->addStmtsp(instrID); + nodep->addStmtsp(insID); nodep->addStmtsp(var_x_task); nodep->addStmtsp(tmp_var_task); } @@ -986,14 +986,14 @@ class InstrumentFunc final : public VNVisitor { //The function is used to further specify the function node created at the module visitor. void visit(AstFunc* nodep) override { if (m_addedFunc == false && nodep == m_funcp && m_current_module != nullptr) { - AstVar* instrID = nullptr; + AstVar* insID = nullptr; AstVar* dpi_trigger = nullptr; AstVar* var_x_func = nullptr; - instrID = new AstVar{nodep->fileline(), VVarType::PORT, "instrID", VFlagChildDType{}, + insID = new AstVar{nodep->fileline(), VVarType::PORT, "insID", VFlagChildDType{}, new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, VSigning::SIGNED, 32, 0}}; - instrID->direction(VDirection::INPUT); + insID->direction(VDirection::INPUT); var_x_func = m_orig_varp->cloneTree(false); var_x_func->varType(VVarType::PORT); @@ -1002,7 +1002,7 @@ class InstrumentFunc final : public VNVisitor { dpi_trigger->varType(VVarType::PORT); dpi_trigger->direction(VDirection::INPUT); - nodep->addStmtsp(instrID); + nodep->addStmtsp(insID); nodep->addStmtsp(dpi_trigger); nodep->addStmtsp(var_x_func); } @@ -1047,7 +1047,7 @@ class InstrumentFunc final : public VNVisitor { void visit(AstVar* nodep) override { if (m_current_module != nullptr && nodep->name() == m_orig_varp->name()) { - m_orig_varp_instMod = nodep; + m_orig_varp_insMod = nodep; } } @@ -1061,13 +1061,13 @@ class InstrumentFunc final : public VNVisitor { static_cast(getMapEntryFaultCase(m_targetKey, m_targetIndex))}; AstVarRef* added_varrefp - = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + = new AstVarRef{nodep->fileline(), m_orig_varp_insMod, VAccess::READ}; nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); nodep->addPinsp(new AstArg{nodep->fileline(), "", new AstParseRef{nodep->fileline(), m_tmp_varp->name()}}); - m_orig_varp_instMod = nullptr; + m_orig_varp_insMod = nullptr; } } @@ -1086,12 +1086,12 @@ class InstrumentFunc final : public VNVisitor { = new AstVarRef{nodep->fileline(), m_dpi_trigger, VAccess::READ}; AstVarRef* added_varrefp - = new AstVarRef{nodep->fileline(), m_orig_varp_instMod, VAccess::READ}; + = new AstVarRef{nodep->fileline(), m_orig_varp_insMod, VAccess::READ}; nodep->addPinsp(new AstArg{nodep->fileline(), "", constp_id}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_triggerp}); nodep->addPinsp(new AstArg{nodep->fileline(), "", added_varrefp}); - m_orig_varp_instMod = nullptr; + m_orig_varp_insMod = nullptr; m_funcrefp = nullptr; } } @@ -1100,21 +1100,21 @@ class InstrumentFunc final : public VNVisitor { //Sets the m_assignw flag to true if the current module is not null. //Necessary for the AstParseRef visitor function to determine if the current node is part of an //assignment. - void visit(AstAssignW* nodep) override { instrAssigns(nodep); } - void visit(AstAssign* nodep) override { instrAssigns(nodep); } - void visit(AstAssignDly* nodep) override { instrAssigns(nodep); } - void visit(AstAssignForce* nodep) override { instrAssigns(nodep); } + void visit(AstAssignW* nodep) override { insAssigns(nodep); } + void visit(AstAssign* nodep) override { insAssigns(nodep); } + void visit(AstAssignDly* nodep) override { insAssigns(nodep); } + void visit(AstAssignForce* nodep) override { insAssigns(nodep); } //ASTPARSE REF VISITOR FUNCTION: - //The function is used to change the parseref nodes to link to the instrumented variable + //The function is used to change the parseref nodes to link to the hook-inserted variable //instead of the original variable. Depending on the direction of the original variable, //different actions are performed: // - If the original variable is not an output variable and the assignment is true, the - //parseref node is changed to link to the instrumented variable. This ensures that the - //instrumented variable is used in the assignment. + //parseref node is changed to link to the hook-inserted variable. This ensures that the + //hook-inserted variable is used in the assignment. // - If the original variable is an input variable, every parseref node is changed to link // to - //the instrumented variable. This ensures that the instrumented variable is used as the new + //the hook-inserted variable. This ensures that the hook-inserted variable is used as the new //input. void visit(AstParseRef* nodep) override { if (m_current_module != nullptr && m_orig_varp != nullptr @@ -1130,24 +1130,24 @@ class InstrumentFunc final : public VNVisitor { public: // CONSTRUCTORS - explicit InstrumentFunc(AstNetlist* nodep) { iterate(nodep); } - ~InstrumentFunc() override = default; + explicit HookInsFunc(AstNetlist* nodep) { iterate(nodep); } + ~HookInsFunc() override = default; }; //################################################################################## -// Instrumentation class functions +// Hook-insertion class functions -// Function to find instrumentation targets and additional information for the instrumentation +// Function to find hook-insertion targets and additional information for the hook-insertion // process -void V3Instrument::findTargets(AstNetlist* nodep) { +void V3InsertHook::findTargets(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstrumentTargetFndr{nodep}; } - V3Global::dumpCheckGlobalTree("instrumentationFinder", 0, dumpTreeEitherLevel() >= 3); + { HookInsTargetFndr{nodep}; } + V3Global::dumpCheckGlobalTree("hookInsertFinder", 0, dumpTreeEitherLevel() >= 3); } -// Function for the actual instrumentation process -void V3Instrument::instrument(AstNetlist* nodep) { +// Function for the actual hook-insertion process +void V3InsertHook::insertHooks(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); - { InstrumentFunc{nodep}; } - V3Global::dumpCheckGlobalTree("instrumentationFunction", 0, dumpTreeEitherLevel() >= 3); + { HookInsFunc{nodep}; } + V3Global::dumpCheckGlobalTree("hookInsertFunction", 0, dumpTreeEitherLevel() >= 3); } diff --git a/src/V3Instrument.h b/src/V3InsertHook.h similarity index 85% rename from src/V3Instrument.h rename to src/V3InsertHook.h index 34ef78c62..6d7e1e628 100644 --- a/src/V3Instrument.h +++ b/src/V3InsertHook.h @@ -14,8 +14,8 @@ // //************************************************************************* -#ifndef VERILATOR_V3INSTRUMENT_H_ -#define VERILATOR_V3INSTRUMENT_H_ +#ifndef VERILATOR_V3INSERTHOOK_H_ +#define VERILATOR_V3INSERTHOOK_H_ #include "config_build.h" #include "verilatedos.h" @@ -24,10 +24,10 @@ class AstNetlist; //========================================================================= -class V3Instrument final { +class V3InsertHook final { public: static void findTargets(AstNetlist* nodep) VL_MT_DISABLED; - static void instrument(AstNetlist* nodep) VL_MT_DISABLED; + static void insertHooks(AstNetlist* nodep) VL_MT_DISABLED; }; #endif // Guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 78310680c..8c6808d97 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1946,7 +1946,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, addIncDirUser(parseFileArg(optdir, string{valp})); }); - DECL_OPTION("-instrument", OnOff, &m_instrument); + DECL_OPTION("-insert-hook", OnOff, &m_insertHook); DECL_OPTION("-dump-signals", OnOff, &m_dumpSignals); parser.finalize(); diff --git a/src/V3Options.h b/src/V3Options.h index 39ac5fa85..7603a3788 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -309,7 +309,7 @@ private: bool m_waiverMultiline = false; // main switch: --waiver-multiline bool m_xInitialEdge = false; // main switch: --x-initial-edge bool m_xmlOnly = false; // main switch: --xml-only - bool m_instrument = false; // main switch: --instrument + bool m_insertHook = false; // main switch: --insert-hook int m_buildJobs = -1; // main switch: --build-jobs, -j int m_coverageExprMax = 32; // main switch: --coverage-expr-max @@ -587,7 +587,7 @@ public: bool xmlOnly() const { return m_xmlOnly; } bool serializeOnly() const { return m_xmlOnly || m_jsonOnly; } bool topIfacesSupported() const { return lintOnly() && !hierarchical(); } - bool instrument() const { return m_instrument; } + bool insertHook() const { return m_insertHook; } int buildJobs() const VL_MT_SAFE { return m_buildJobs; } int convergeLimit() const { return m_convergeLimit; } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index c307b806e..4caf3e1af 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -65,7 +65,7 @@ #include "V3HierBlock.h" #include "V3Inline.h" #include "V3Inst.h" -#include "V3Instrument.h" +#include "V3InsertHook.h" #include "V3Interface.h" #include "V3Life.h" #include "V3LifePost.h" @@ -156,11 +156,11 @@ static void process() { if (v3Global.opt.dumpSignals()) { V3DumpSignals::dumpSignals(v3Global.rootp()); } - // Instrument Design with the configurations given in .vlt file - if (v3Global.opt.instrument()) { + // Hook-insert design with the configurations given in .vlt file + if (v3Global.opt.insertHook()) { v3Global.dpi(true); - V3Instrument::findTargets(v3Global.rootp()); - V3Instrument::instrument(v3Global.rootp()); + V3InsertHook::findTargets(v3Global.rootp()); + V3InsertHook::insertHooks(v3Global.rootp()); } // Convert parseref's to varrefs, and other directly post parsing fixups diff --git a/src/verilog.l b/src/verilog.l index eff7e8d50..6d0f38afe 100644 --- a/src/verilog.l +++ b/src/verilog.l @@ -123,7 +123,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5} "hier_params" { FL; return yVLT_HIER_PARAMS; } "hier_workers" { FL; return yVLT_HIER_WORKERS; } "inline" { FL; return yVLT_INLINE; } - "instrument" { FL; return yVLT_INSTRUMENT; } + "insert_hook" { FL; return yVLT_INSERTHOOK; } "isolate_assignments" { FL; return yVLT_ISOLATE_ASSIGNMENTS; } "lint_off" { FL; return yVLT_LINT_OFF; } "lint_on" { FL; return yVLT_LINT_ON; } diff --git a/src/verilog.y b/src/verilog.y index 93276c0a2..a83fd317d 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -250,7 +250,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"}) %token yVLT_HIER_PARAMS "hier_params" %token yVLT_HIER_WORKERS "hier_workers" %token yVLT_INLINE "inline" -%token yVLT_INSTRUMENT "instrument" +%token yVLT_INSERTHOOK "insert_hook" %token yVLT_ISOLATE_ASSIGNMENTS "isolate_assignments" %token yVLT_LINT_OFF "lint_off" %token yVLT_LINT_ON "lint_on" @@ -8013,8 +8013,8 @@ vltItem: { /* Historical, now has no effect */ } | vltInlineFront vltDModuleE vltDFTaskE { V3Control::addInline($1, *$2, *$3, $1); } - | yVLT_INSTRUMENT yVLT_D_CALLBACK yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING - { V3Control::addInstrumentCfg($1, *$3, $5->toSInt(), *$7); } + | yVLT_INSERTHOOK yVLT_D_CALLBACK yaSTRING yVLT_D_ID yaINTNUM yVLT_D_TARGET yaSTRING + { V3Control::addHookInsCfg($1, *$3, $5->toSInt(), *$7); } | yVLT_COVERAGE_BLOCK_OFF vltDFile { V3Control::addCoverageBlockOff(*$2, 0); } | yVLT_COVERAGE_BLOCK_OFF vltDFile yVLT_D_LINES yaINTNUM From 8d597ab62c77777e05bf517829f09cab7f9877af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Thu, 27 Nov 2025 11:30:13 +0100 Subject: [PATCH 17/20] feat: Extendend .json content --- src/V3AstNodeOther.h | 4 ++++ src/V3AstNodes.cpp | 2 ++ src/V3InsertHook.cpp | 1 + 3 files changed, 7 insertions(+) diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index 36913ebd4..ff391ef43 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -1863,6 +1863,7 @@ class AstVar final : public AstNode { bool m_substConstOnly : 1; // Only substitute if constant bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam bool m_trace : 1; // Trace this variable + bool m_isHookInserted : 1; // This variable is instrumented for coverage bool m_isLatched : 1; // Not assigned in all control paths of combo always bool m_isForceable : 1; // May be forced/released externally from user C code bool m_isForcedByCode : 1; // May be forced/released from AstAssignForce/AstRelease @@ -1913,6 +1914,7 @@ class AstVar final : public AstNode { m_substConstOnly = false; m_overridenParam = false; m_trace = false; + m_isHookInserted = false; m_isLatched = false; m_isForceable = false; m_isForcedByCode = false; @@ -2055,6 +2057,8 @@ public: bool gotNansiType() { return m_gotNansiType; } void hasStrengthAssignment(bool flag) { m_hasStrengthAssignment = flag; } bool hasStrengthAssignment() { return m_hasStrengthAssignment; } + void hasHookInserted(bool flag) { m_isHookInserted = flag; } + bool isHookInserted() const { return m_isHookInserted; } void isDpiOpenArray(bool flag) { m_isDpiOpenArray = flag; } bool isDpiOpenArray() const VL_MT_SAFE { return m_isDpiOpenArray; } bool isHideLocal() const { return m_isHideLocal; } diff --git a/src/V3AstNodes.cpp b/src/V3AstNodes.cpp index d7ed985dc..a66b0298c 100644 --- a/src/V3AstNodes.cpp +++ b/src/V3AstNodes.cpp @@ -2790,6 +2790,8 @@ void AstVar::dumpJson(std::ostream& str) const { dumpJsonBoolFunc(str, attrSFormat); dumpJsonBoolFunc(str, ignorePostWrite); dumpJsonBoolFunc(str, ignoreSchedWrite); + dumpJsonBoolFunc(str, isHookInserted); + dumpJsonNum(str, "width", width()); dumpJsonGen(str); } void AstScope::dump(std::ostream& str) const { diff --git a/src/V3InsertHook.cpp b/src/V3InsertHook.cpp index c027ea13c..9183db557 100644 --- a/src/V3InsertHook.cpp +++ b/src/V3InsertHook.cpp @@ -441,6 +441,7 @@ class HookInsTargetFndr final : public VNVisitor { AstVar* varp = nodep->cloneTree(false); varp->name("tmp_" + nodep->name()); varp->origName("tmp_" + nodep->name()); + varp->hasHookInserted(true); varp->trace(true); if (varp->varType() == VVarType::WIRE) { varp->varType(VVarType::VAR); } setVar(nodep, varp, m_target); From 33568f33faf14723cd3850ca3419fc56a6745b53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 3 Dec 2025 10:06:36 +0100 Subject: [PATCH 18/20] fix: Addressed instance issues --- src/V3Control.cpp | 13 +++-- src/V3Control.h | 2 +- src/V3InsertHook.cpp | 123 ++++++++++++++++++++++++++++++++----------- src/Verilator.cpp | 2 +- 4 files changed, 99 insertions(+), 41 deletions(-) diff --git a/src/V3Control.cpp b/src/V3Control.cpp index 0fc2c86e6..499bcf41a 100644 --- a/src/V3Control.cpp +++ b/src/V3Control.cpp @@ -652,14 +652,13 @@ public: return {prefix, varTarget}; } // Add the hook-insertion config data to the map to create the initial map (Used in verilog.y) - void addHookInsCfg(FileLine* fl, const string& insFunction, int insID, - const string& target) { + void addHookInsCfg(FileLine* fl, const string& insFunction, int insID, const string& target) { // Error MSG if the hook-insertion of the top module is not possible if ((std::count(target.begin(), target.end(), '.') < 2)) { - v3fatal( - "In .vlt defined target tries to insert-hook to the highest MODULE, which is not possible!" - " ... Target string: " - << target); + v3fatal("In .vlt defined target tries to insert-hook to the highest MODULE, which is " + "not possible!" + " ... Target string: " + << target); } // Implement custom iterator to remove the last part of the target and insert it into the // vector of the map If the target string is the same as one already in the map, push the @@ -740,7 +739,7 @@ void V3Control::addModulePragma(const string& module, VPragmaType pragma) { } void V3Control::addHookInsCfg(FileLine* fl, const string& insfunc, int insID, - const string& target) { + const string& target) { V3ControlResolver::s().addHookInsCfg(fl, insfunc, insID, target); } diff --git a/src/V3Control.h b/src/V3Control.h index cc93ff468..3ba3cc8b8 100644 --- a/src/V3Control.h +++ b/src/V3Control.h @@ -71,7 +71,7 @@ public: const string& match); static void addInline(FileLine* fl, const string& module, const string& ftask, bool on); static void addHookInsCfg(FileLine* fl, const string& insfunc, int insID, - const string& target); + const string& target); static std::map& getHookInsCfg(); static void addModulePragma(const string& module, VPragmaType pragma); static void addProfileData(FileLine* fl, const string& hierDpi, uint64_t cost); diff --git a/src/V3InsertHook.cpp b/src/V3InsertHook.cpp index 9183db557..255d8a58a 100644 --- a/src/V3InsertHook.cpp +++ b/src/V3InsertHook.cpp @@ -139,19 +139,19 @@ class HookInsTargetFndr final : public VNVisitor { void addParam(AstModule* modp) { AstVar* paramp = new AstVar{modp->fileline(), VVarType::GPARAM, "HOOKINS", VFlagChildDType{}, nullptr}; - paramp->valuep(new AstConst{modp->fileline(), AstConst::Signed32{}, 0}); + paramp->valuep(new AstConst{modp->fileline(), AstConst::String{}, ""}); paramp->dtypep(paramp->valuep()->dtypep()); paramp->ansi(true); modp->addStmtsp(paramp); } // Helper function for adding the parameters into the tree - void addPin(AstCell* cellp, bool isInsPath) { + void addPin(AstCell* cellp, bool isInsPath, const string& target) { int pinnum = 0; if (isInsPath) { for (AstNode* n = cellp->pinsp(); n; n = n->nextp()) { pinnum++; } AstPin* pinp = new AstPin{cellp->fileline(), pinnum + 1, "HOOKINS", // The pin is set to 1 to enable the hook-insertion path - new AstConst{cellp->fileline(), AstConst::Signed32{}, 1}}; + new AstConst{cellp->fileline(), AstConst::String{}, target}}; pinp->param(true); cellp->addParamsp(pinp); } else { @@ -372,17 +372,17 @@ class HookInsTargetFndr final : public VNVisitor { m_foundModp = false; m_initModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { addPin(nodep, false); } + if (!hasPin(nodep)) { addPin(nodep, false, m_target); } multCellForModp(nodep); - setCell(nodep, m_target); + setCell(nodep->cloneTree(false, false), m_target); } else if (targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; m_foundModp = false; m_initModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { addPin(nodep, true); } + if (!hasPin(nodep)) { addPin(nodep, true, m_target); } multCellForModp(nodep); - setCell(nodep, m_target); + setCell(nodep->cloneTree(false, false), m_target); } else if (!m_foundCellp && !VN_IS(nodep->nextp(), Cell)) { v3error("Verilator-configfile': could not find initial 'instance' in " "'topModule.instance.__' ... Target string: '" @@ -395,15 +395,15 @@ class HookInsTargetFndr final : public VNVisitor { m_foundCellp = true; m_foundModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { addPin(nodep, false); } + if (!hasPin(nodep)) { addPin(nodep, false, m_target); } multCellForModp(nodep); - setCell(nodep, m_target); + setCell(nodep->cloneTree(false, false), m_target); } else if (m_modp != nullptr && targetHasPrefix(m_currHier + "." + nodep->name(), m_target)) { m_foundCellp = true; m_foundModp = false; m_currHier = m_currHier + "." + nodep->name(); - if (!hasPin(nodep)) { addPin(nodep, false); } + if (!hasPin(nodep)) { addPin(nodep, false, m_target); } multCellForModp(nodep); } } @@ -417,8 +417,7 @@ class HookInsTargetFndr final : public VNVisitor { //the original version are added to the hook-insertion config map. void visit(AstVar* nodep) override { if (m_targetModp != nullptr) { - const HookInsertTarget& target - = V3Control::getHookInsCfg().find(m_currHier)->second; + const HookInsertTarget& target = V3Control::getHookInsCfg().find(m_currHier)->second; for (const auto& entry : target.entries) { if (nodep->name() == entry.varTarget) { int width = 0; @@ -566,6 +565,11 @@ class HookInsFunc final : public VNVisitor { } return nullptr; } + bool isTarget(const std::string& key) { + const auto& map = V3Control::getHookInsCfg(); + const auto insCfg = map.find(key); + return insCfg != map.end(); + } // Check if the given module node pointer is an hook-inserted module entry in the configuration // map for the given key bool isInsModEntry(AstModule* nodep, const std::string& key) { @@ -637,7 +641,8 @@ class HookInsFunc final : public VNVisitor { return -1; } } - // Get the callback function name from the hook-insertion for the given key in the configuration map + // Get the callback function name from the hook-insertion for the given key in the + // configuration map string getMapEntryFunction(const std::string& key, size_t index) { const auto& map = V3Control::getHookInsCfg(); const auto insCfg = map.find(key); @@ -649,6 +654,13 @@ class HookInsFunc final : public VNVisitor { return ""; } } + string cleanString(const string& str) { + if (str.size() >= 2 && str.front() == '"' && str.back() == '"') { + return str.substr(1, str.size() - 2); + } else { + return ""; + } + } // Set the done flag for the given module node pointer in the configuraiton map void setDone(AstModule* nodep) { auto& insCfg = V3Control::getHookInsCfg(); @@ -852,8 +864,7 @@ class HookInsFunc final : public VNVisitor { if (VN_IS(n, Port)) { m_pinnum = VN_CAST(n, Port)->pinNum(); } } iterateChildren(nodep); - } else if ((std::count(m_targetKey.begin(), m_targetKey.end(), '.') > 0) - && (isPointingModEntry(nodep) || isTopModEntry(nodep)) + } else if ((isPointingModEntry(nodep) || isTopModEntry(nodep)) && !hasMultiple(m_targetKey)) { m_current_module_cell_check = nodep; AstCell* insCellp = getMapEntryCell(m_targetKey); @@ -864,14 +875,57 @@ class HookInsFunc final : public VNVisitor { AstCell* insCellp = getMapEntryCell(m_targetKey)->cloneTree(false); insCellp->modp(getMapEntryInsModule(m_targetKey)); for (AstNode* n = insCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + bool addedInitGenIf = false; + bool breakOuter = false; + std::string condValue = ""; m_insGenBlock = new AstGenBlock{nodep->fileline(), "", insCellp, false}; - AstGenIf* genifp = new AstGenIf{ - nodep->fileline(), new AstParseRef{nodep->fileline(), "HOOKINS"}, - m_insGenBlock, - new AstGenBlock{nodep->fileline(), "", - getMapEntryCell(m_targetKey)->cloneTree(false), false}}; - - nodep->addStmtsp(genifp); + for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { + if (VN_IS(n, GenIf)) { + condValue = cleanString(VN_CAST(n, GenIf)->condp()->op2p()->name()); + if (condValue != "" && isTarget(condValue)) { addedInitGenIf = true; } + for (AstNode* m = n; VN_CAST(m, GenIf)->elsesp()->op2p(); + m = VN_CAST(m, GenIf)->elsesp()->op2p()) { + if (VN_IS(m, GenIf)) { + condValue + = cleanString(VN_CAST(m, GenIf)->condp()->op2p()->name()); + if (condValue == m_targetKey) { + breakOuter = true; + break; + } + if (VN_CAST(m, GenIf)->elsesp()->op2p()->type() != VNType::GenIf) { + AstGenIf* genifp = new AstGenIf{ + nodep->fileline(), + new AstEq{nodep->fileline(), + new AstParseRef{nodep->fileline(), "HOOKINS"}, + new AstConst{nodep->fileline(), + AstConst::String{}, m_targetKey}}, + m_insGenBlock, + new AstGenBlock{ + nodep->fileline(), "", + getMapEntryCell(m_targetKey)->cloneTree(false), + false}}; + VN_CAST(m, GenIf)->elsesp()->op2p()->replaceWith(genifp); + breakOuter = true; + break; + } + } else { + v3fatal("Something went wrong!"); + } + } + } + if (breakOuter) { break; } + } + if (!addedInitGenIf) { + AstGenIf* genifp = new AstGenIf{ + nodep->fileline(), + new AstEq{ + nodep->fileline(), new AstParseRef{nodep->fileline(), "HOOKINS"}, + new AstConst{nodep->fileline(), AstConst::String{}, m_targetKey}}, + m_insGenBlock, + new AstGenBlock{nodep->fileline(), "", + getMapEntryCell(m_targetKey)->cloneTree(false), false}}; + nodep->addStmtsp(genifp); + } iterateChildren(m_insGenBlock); iterateChildren(nodep); } @@ -923,16 +977,21 @@ class HookInsFunc final : public VNVisitor { //by the ASTPIN VISITOR FUNCTION. // - If hasMultiple is true, the cell is unlinked from the back and deleted. // This ensures that the cell is not used anymore in the module, and the conditional - //statment deciding between the hook-inserted and the original cell can be created/used. A third - //action is performed if the variable beeing hook-inserted is an ouput variable. In this case - //the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. + //statment deciding between the hook-inserted and the original cell can be created/used. A + //third action is performed if the variable beeing hook-inserted is an ouput variable. In this + //case the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. void visit(AstCell* nodep) override { - if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) - && nodep == getMapEntryCell(m_targetKey)) { + bool nodeHasName = false; + bool nodeHasCorrectBackp = false; + bool isCorrectMultCell = false; + nodeHasName = (nodep->name() == getMapEntryCell(m_targetKey)->name()); + nodeHasCorrectBackp = (nodep->backp()->type() != VNType::GenBlock); + isCorrectMultCell = nodeHasName && nodeHasCorrectBackp; + if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) && nodeHasName) { nodep->modp(getMapEntryInsModule(m_targetKey)); if (m_orig_varp->direction() == VDirection::OUTPUT) { iterateChildren(nodep); } } else if (m_current_module_cell_check != nullptr && hasMultiple(m_targetKey) - && nodep == getMapEntryCell(m_targetKey)) { + && isCorrectMultCell) { nodep->unlinkFrBack(); nodep->deleteTree(); } else if (m_insGenBlock != nullptr && nodep->modp() == getMapEntryInsModule(m_targetKey) @@ -965,8 +1024,8 @@ class HookInsFunc final : public VNVisitor { AstVar* tmp_var_task = nullptr; insID = new AstVar{nodep->fileline(), VVarType::PORT, "insID", VFlagChildDType{}, - new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, - VSigning::SIGNED, 32, 0}}; + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; insID->direction(VDirection::INPUT); var_x_task = m_orig_varp->cloneTree(false); @@ -992,8 +1051,8 @@ class HookInsFunc final : public VNVisitor { AstVar* var_x_func = nullptr; insID = new AstVar{nodep->fileline(), VVarType::PORT, "insID", VFlagChildDType{}, - new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, - VSigning::SIGNED, 32, 0}}; + new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT, + VSigning::SIGNED, 32, 0}}; insID->direction(VDirection::INPUT); var_x_func = m_orig_varp->cloneTree(false); diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 4caf3e1af..232a2b67b 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -64,8 +64,8 @@ #include "V3Graph.h" #include "V3HierBlock.h" #include "V3Inline.h" -#include "V3Inst.h" #include "V3InsertHook.h" +#include "V3Inst.h" #include "V3Interface.h" #include "V3Life.h" #include "V3LifePost.h" From 84c51206bc8c9a339337216b1f107bbfc7fd904c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 3 Dec 2025 13:19:16 +0100 Subject: [PATCH 19/20] style: Revised comments --- src/V3InsertHook.cpp | 305 ++++++++++--------------------------------- 1 file changed, 70 insertions(+), 235 deletions(-) diff --git a/src/V3InsertHook.cpp b/src/V3InsertHook.cpp index 255d8a58a..d4d2308b7 100644 --- a/src/V3InsertHook.cpp +++ b/src/V3InsertHook.cpp @@ -39,31 +39,29 @@ VL_DEFINE_DEBUG_FUNCTIONS; //################################################################################## -// Hook-insertion class finder +// Collect nodes and data from the AST for hook-insertion class HookInsTargetFndr final : public VNVisitor { AstNetlist* m_netlist - = nullptr; // Enable traversing from the beginning if the visitor is to deep - AstNodeModule* m_cellModp = nullptr; // Stores the modulep of a Cell node - AstModule* m_modp = nullptr; // Stores the current modulep the visitor is looking at - AstModule* m_targetModp = nullptr; // Stores the targeted modulep - bool m_error = false; // Displays if there was already an error message earlier - bool m_foundCellp = false; // If the visitor found the relevant instance - bool m_foundModp = false; // If the visitor found the relevant model - bool m_foundVarp = false; // If the visitor found the relevant variable + = nullptr; // Used for traversing AST from the beginning if the visitor is to deep + AstNodeModule* m_cellModp = nullptr; + AstModule* m_modp = nullptr; + AstModule* m_targetModp = nullptr; + bool m_error = false; + bool m_foundCellp = false; + bool m_foundModp = false; + bool m_foundVarp = false; bool m_initModp = true; // If the visitor is in the first module node of the netlist size_t m_insIdx = 0; - string m_currHier; // Stores the current hierarchy of the visited nodes (Module, Cell, Var) - string m_target; // Stores the currently visited target string from the config map + string m_currHier; + string m_target; // METHODS - //---------------------------------------------------------------------------------- AstModule* findModp(AstNetlist* netlist, AstModule* modp) { for (AstNode* n = netlist->op1p(); n; n = n->nextp()) { if (VN_IS(n, Module) && VN_CAST(n, Module) == modp) { return VN_CAST(n, Module); } } return nullptr; } - // Helper function to compare the target string starts with the given prefix bool cmpPrefix(const string& prefix, const string& target) { if (target.compare(0, prefix.size(), prefix) == 0 && (target.size() == prefix.size() || target[prefix.size()] == '.')) { @@ -71,21 +69,18 @@ class HookInsTargetFndr final : public VNVisitor { } return false; } - // Helper function to check if a parameter was already added to the tree previously bool hasParam(AstModule* modp) { for (AstNode* n = modp->op2p(); n; n = n->nextp()) { if (n->name() == "HOOKINS") { return true; } } return false; } - // Helper function to check if a pin was already added to the tree previously bool hasPin(AstCell* cellp) { for (AstNode* n = cellp->paramsp(); n; n = n->nextp()) { if (n->name() == "HOOKINS") { return true; } } return false; } - // Check if the multipleCellps flag is set for the given target bool hasMultiple(const std::string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); @@ -112,18 +107,17 @@ class HookInsTargetFndr final : public VNVisitor { bool targetHasPointingMod(const string& pointingModuleName, const string& target) { return pointingModuleName == reduce2Depth(split(target), KeyDepth::RelevantModule); } - // Check if the given prefix matches the beginning of the current target string bool targetHasPrefix(const string& prefix, const string& target) { return cmpPrefix(prefix, target); } - // Helper Function to split a string by '.' and return a vector of tokens + // Split given string by '.' and return a vector of tokens std::vector split(const std::string& str) { static const std::regex dot_regex("\\."); std::sregex_token_iterator iter(str.begin(), str.end(), dot_regex, -1); std::sregex_token_iterator end; return std::vector(iter, end); } - // Helper function to reduce a given key to a certain hierarchy level. + // Reduce given key to a certain hierarchy level. enum class KeyDepth { TopModule = 0, RelevantModule = 1, Instance = 2, FullKey = 3 }; string reduce2Depth(std::vector keyTokens, KeyDepth hierarchyLevel) { std::string reducedKey = keyTokens[0]; @@ -135,7 +129,6 @@ class HookInsTargetFndr final : public VNVisitor { return reducedKey; } } - // Helper function for adding the parameters into the tree void addParam(AstModule* modp) { AstVar* paramp = new AstVar{modp->fileline(), VVarType::GPARAM, "HOOKINS", VFlagChildDType{}, nullptr}; @@ -144,7 +137,6 @@ class HookInsTargetFndr final : public VNVisitor { paramp->ansi(true); modp->addStmtsp(paramp); } - // Helper function for adding the parameters into the tree void addPin(AstCell* cellp, bool isInsPath, const string& target) { int pinnum = 0; if (isInsPath) { @@ -162,13 +154,11 @@ class HookInsTargetFndr final : public VNVisitor { cellp->addParamsp(pinp); } } - // Edit the hook-insertion data for the cell in the map void editInsData(AstCell* cellp, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.cellp = cellp; } } - // Edit the hook-insertion data for the pointing module in the map void editInsData(AstModule* modulep, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); @@ -187,13 +177,11 @@ class HookInsTargetFndr final : public VNVisitor { setMultiple(m_target); } } - // Insert the cell node that is/will pointing/point to the targeted module void setCell(AstCell* cellp, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.cellp = cellp; } } - // Insert the original and hook-inserted module nodes to the map void setInsModule(AstModule* origModulep, AstModule* insModulep, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); @@ -202,32 +190,26 @@ class HookInsTargetFndr final : public VNVisitor { it->second.insModulep = insModulep; } } - // Set the multipleCellps flag void setMultiple(const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.multipleCellps = true; } } - // Insert the module node that includes the cell pointing to the targeted module - // to the map void setPointingMod(AstModule* modulep, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.pointingModulep = modulep; } } - // Set the processed flag void setProcessed(const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.processed = true; } } - // Insert the top module node of the netlist to the map void setTopMod(AstModule* modulep, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); if (it != insCfg.end()) { it->second.topModulep = modulep; } } - // Insert the original and hook-inserted variable nodes to the map void setVar(AstVar* varp, AstVar* insVarp, const string& target) { auto& insCfg = V3Control::getHookInsCfg(); auto it = insCfg.find(target); @@ -244,33 +226,10 @@ class HookInsTargetFndr final : public VNVisitor { } // VISITORS - //---------------------------------------------------------------------------------- - - //ASTMODULE VISITOR FUNCTION: - //Iterates over the existing module nodes in the netlist. - //For the first module in the netlist the node name is checked if it is at the first position - //in the target string provided by the configuration file. If not an error is thown, otherwise - //the modules is checked for an already existing HOOKINS parameter. If there is no - //HOOKINS parameter present we add it to the module. This parameter is used to control the - //hook-insertion of the target. The module is then added to the map of the hook-insertion - //configs as the top module. Additionally the hierarchy the function viewed is currently add is - //initialized with the module name. This module hierarchy is used to identify the correct - //target path in the netlist. The function iterates over the children of the module, with the - //Cells and Vars beeing the relevant targets. - - //After the iteration of the children the m_modp variable needs to be set by the Cell visitor - //to continue or there needs no suitable cell to be found. (See CELL VISITOR FUNCTION & VAR - //VISITOR FUNCTION) Since the module from the m_modp can appear earlier in the tree the - //fundModp function is used to iterate over the netlift from the beginning to find the module. - //The module node displayed by the m_modp variable is then checked if this is the module - //containing the target variable (relevant module) or if it the module containing the cell - //pointing to the relevant module (pointing module). If the module node suits one of these two - //conditions the module nodes are added to the hook-insertion configs map. Independetly from - //these conditions the HOOKINS parameter is added to the module nodes in the target path. - //This parameter is used to control the hook-insertion of the target. void visit(AstModule* nodep) override { if (m_initModp) { if (targetHasTop(nodep->name(), m_target)) { + // Add decision parameters to the module if not present m_foundModp = true; m_modp = nodep; m_currHier = nodep->name(); @@ -280,8 +239,9 @@ class HookInsTargetFndr final : public VNVisitor { m_foundCellp = true; // Set to true since there is no Instance that the cell // visitor could find } + // Store top module pointer for later setTopMod(nodep, m_target); - iterateChildren(nodep); + iterateChildren(nodep); // Continue to Cell/Var nodes } else if (!m_foundModp && nodep->name() == "@CONST-POOL@") { v3error("Verilator-configfile': could not find initial 'module' in " "'module.instance.__'" @@ -290,7 +250,7 @@ class HookInsTargetFndr final : public VNVisitor { m_initModp = false; m_error = true; } - } else if (m_cellModp != nullptr + } else if (m_cellModp != nullptr // Find module pointed to by the cell from cell visitor && (nodep = findModp(m_netlist, VN_CAST(m_cellModp, Module))) != nullptr) { if (targetHasFullName(m_currHier, m_target)) { AstModule* insModp = nullptr; @@ -319,7 +279,7 @@ class HookInsTargetFndr final : public VNVisitor { insModp->name(nodep->name() + "__hookIns__" + std::to_string(m_insIdx)); if (hasMultiple(m_target)) { insModp->inLibrary(true); } setInsModule(nodep, insModp, m_target); - iterateChildren(nodep); + iterateChildren(nodep); // Continue to var node } else if (targetHasPointingMod(m_currHier, m_target)) { m_foundModp = true; m_foundCellp = false; @@ -327,14 +287,14 @@ class HookInsTargetFndr final : public VNVisitor { m_cellModp = nullptr; if (!hasParam(nodep)) { addParam(nodep); } setPointingMod(nodep, m_target); - iterateChildren(nodep); + iterateChildren(nodep); // Continue to cell } else if (targetHasPrefix(m_currHier, m_target)) { m_foundModp = true; m_foundCellp = false; m_modp = nodep; m_cellModp = nullptr; if (!hasParam(nodep)) { addParam(nodep); } - iterateChildren(nodep); + iterateChildren(nodep); // Continue to cell } } else if (!m_error && !m_foundCellp) { v3error("Verilator-configfile: could not find 'instance' in " @@ -347,24 +307,6 @@ class HookInsTargetFndr final : public VNVisitor { } } - //ASTCELL VISITOR FUNCTION: - //This cell visitor function is called if the module visitor function found a module that - //matches the target string from the config. The first function call should be when visiting - //the initial module in the netlist. When a cell is found that matches the target string and is - //not marked as found, the current hierarchy is updated and the cell marked as found. - //Additionally, if this is the cell in the initial module, the initial module flag is set to - //false. The in the current module existing cells are checked if there are multiple cells - //linking to the next module in the target string. After that the m_modp is updated to match - //the cell's module pointer, which is needed for the next call of the module visitor. Next the - //pin for the HOOKINS parameter is added to the cell. This parameter is added either as a - //constant or as a reference, depending on the traversal stage. If there are multiple cells - //linking to the next module in the target string, the multiple flag is set in the - //hook-insertion config map. For the inistial module the found cell is then added to the - //hook-insertion configuration map with the current hierarchy as the target path. Otherwise - //the cell is added to the hook-insertion configuration map, when the current hierarchy with - //the cell name fully matches a target path, with the last two entrances removed (Module, Var). - //This function ensures that the correct cells in the design hierarchy are hook-inserted and - //tracked, supporting both unique and repeated module instances. void visit(AstCell* nodep) override { if (m_initModp) { if (targetHasFullName(m_currHier + "." + nodep->name(), m_target)) { @@ -408,19 +350,14 @@ class HookInsTargetFndr final : public VNVisitor { } } - //ASTVAR VISITOR FUNCTION: - //The var visitor function is used to find the variable that matches the target string from the - //config. This is only done if the Cell visitor does not find a matching cell in the current - //module of the target hierarchy. Since we therefore know that we will not traverse any further - //in the hierarchy of the model, we can check for this variable. If a variable is found, with - //its name added to the current hierarchy, that siuts the target string, an edited version and - //the original version are added to the hook-insertion config map. void visit(AstVar* nodep) override { if (m_targetModp != nullptr) { const HookInsertTarget& target = V3Control::getHookInsCfg().find(m_currHier)->second; for (const auto& entry : target.entries) { + // Go over all var targets if in same module if (nodep->name() == entry.varTarget) { int width = 0; + // Check for if target var is supported AstBasicDType* basicp = nodep->basicp(); bool literal = basicp->isLiteralType(); bool implicit = basicp->implicit(); @@ -469,12 +406,14 @@ public: explicit HookInsTargetFndr(AstNetlist* nodep) { const auto& insCfg = V3Control::getHookInsCfg(); for (const auto& pair : insCfg) { + // Set initial flag values m_netlist = nodep; m_target = pair.first; m_initModp = true; m_currHier = ""; iterate(nodep); setProcessed(m_target); + // Reset flags m_foundModp = false; m_foundCellp = false; m_foundVarp = false; @@ -488,42 +427,35 @@ public: }; //################################################################################## -// Hook-insertion class functions +// Do the hook-insertion transformations class HookInsFunc final : public VNVisitor { - bool m_assignw = false; // Flag if a assignw exists in the netlist + bool m_assignw = false; bool m_assignNode = false; // Set to true to indicate that the visitor is in an assign - bool m_addedport = false; // Flag if a port was already added - bool m_addedTask = false; // Flag if a task was already added - bool m_addedFunc = false; // Flag if a function was already added - bool m_interface = false; // Flag if the ParseRef node is part of an interface - int m_pinnum = 0; // Pinnumber for the new Port nodes - string m_targetKey; // Stores the target string from the hook-insertion config + bool m_addedport = false; + bool m_addedTask = false; + bool m_addedFunc = false; + int m_pinnum = 0; + string m_targetKey; string m_task_name; - size_t m_targetIndex = 0; // Index of the target variable in the hook-insertion config - AstAlways* m_alwaysp = nullptr; // Stores the added always node - AstAssignW* m_assignwp = nullptr; // Stores the added assignw node - AstGenBlock* m_insGenBlock - = nullptr; // Store the GenBlock node for hook-insertion hierarchy check - AstTask* m_taskp = nullptr; // // Stores the created task node - AstFunc* m_funcp = nullptr; // Stores the created function node - AstFuncRef* m_funcrefp = nullptr; // Stores the created funcref node - AstLoop* m_loopp = nullptr; // Stores the created loop pointer - AstTaskRef* m_taskrefp = nullptr; // Stores the created taskref node - AstModule* m_current_module = nullptr; // Stores the currenty visited module + size_t m_targetIndex = 0; + AstAlways* m_alwaysp = nullptr; + AstAssignW* m_assignwp = nullptr; + AstGenBlock* m_insGenBlock = nullptr; + AstTask* m_taskp = nullptr; + AstFunc* m_funcp = nullptr; + AstFuncRef* m_funcrefp = nullptr; + AstLoop* m_loopp = nullptr; + AstTaskRef* m_taskrefp = nullptr; + AstModule* m_current_module = nullptr; AstModule* m_current_module_cell_check = nullptr; // Stores the module node(used by cell visitor) - AstVar* m_tmp_varp = nullptr; // Stores the hook-inserted variable node - AstVar* m_orig_varp = nullptr; // Stores the original variable node - AstVar* m_orig_varp_insMod - = nullptr; // Stores the original variable node in hook-inserted module node - AstVar* m_dpi_trigger - = nullptr; // Stores the variable noded for the dpi-trigger, which ensures the changing of - // a signal and the execution of the DPI function - AstPort* m_orig_portp = nullptr; // Stores the original port node + AstVar* m_tmp_varp = nullptr; + AstVar* m_orig_varp = nullptr; + AstVar* m_orig_varp_insMod = nullptr; + AstVar* m_dpi_trigger = nullptr; // Trigger ensuring changing execution of the DPI function + AstPort* m_orig_portp = nullptr; // METHODS - //---------------------------------------------------------------------------------- - // Find the relevant hook-insertion config in the map corresponding to the given key const HookInsertTarget* getInsCfg(const std::string& key) { const auto& map = V3Control::getHookInsCfg(); auto insCfg = map.find(key); @@ -533,23 +465,18 @@ class HookInsFunc final : public VNVisitor { return nullptr; } } - // Get the Cell nodep pointer from the configuration map for the given key AstCell* getMapEntryCell(const std::string& key) { if (auto cfg = getInsCfg(key)) { return cfg->cellp; } return nullptr; } - // Get the hook-inserted Module node pointer from the configuration map for the given key AstModule* getMapEntryInsModule(const std::string& key) { if (auto cfg = getInsCfg(key)) { return cfg->insModulep; } return nullptr; } - // Get the Module node pointer pointing to the hook-inserted/original module from the - // configuration map for the given key AstModule* getMapEntryPointingModule(const std::string& key) { if (auto cfg = getInsCfg(key)) { return cfg->pointingModulep; } return nullptr; } - // Get the hook-inserted variable node pointer from the configuration map for the given key AstVar* getMapEntryInsVar(const std::string& key, size_t index) { if (auto cfg = getInsCfg(key)) { const auto& entries = cfg->entries; @@ -557,7 +484,6 @@ class HookInsFunc final : public VNVisitor { } return nullptr; } - // Get the original variable node pointer from the configuration map for the given key AstVar* getMapEntryVar(const std::string& key, size_t index) { if (auto cfg = getInsCfg(key)) { const auto& entries = cfg->entries; @@ -570,8 +496,6 @@ class HookInsFunc final : public VNVisitor { const auto insCfg = map.find(key); return insCfg != map.end(); } - // Check if the given module node pointer is an hook-inserted module entry in the configuration - // map for the given key bool isInsModEntry(AstModule* nodep, const std::string& key) { const auto& map = V3Control::getHookInsCfg(); const auto insCfg = map.find(key); @@ -581,7 +505,6 @@ class HookInsFunc final : public VNVisitor { return false; } } - // Check if the given module node pointer is the top module entry in the configuration map bool isTopModEntry(AstModule* nodep) { auto& insCfg = V3Control::getHookInsCfg(); for (const auto& pair : insCfg) { @@ -589,7 +512,6 @@ class HookInsFunc final : public VNVisitor { } return false; } - // Check if the given module node pointer is the pointing module entry in the configuration map bool isPointingModEntry(AstModule* nodep) { auto& insCfg = V3Control::getHookInsCfg(); for (const auto& pair : insCfg) { @@ -597,7 +519,6 @@ class HookInsFunc final : public VNVisitor { } return false; } - // Check if the given module node pointer has already been hook-inserted/done flag has been set bool isDone(AstModule* nodep) { auto& insCfg = V3Control::getHookInsCfg(); for (const auto& pair : insCfg) { @@ -605,7 +526,6 @@ class HookInsFunc final : public VNVisitor { } return true; } - // Check if the multipleCellps flag is set for the given key in the configuration map bool hasMultiple(const std::string& key) { const auto& map = V3Control::getHookInsCfg(); const auto insCfg = map.find(key); @@ -615,21 +535,18 @@ class HookInsFunc final : public VNVisitor { return false; } } - // Check if the module and instances defined in the target string were found in - // the previous step + // Check if issues happend during collection in HookInsTargetFndr bool hasNullptr(const std::pair& pair) { bool moduleNullptr = pair.second.origModulep == nullptr; bool cellNullptr = pair.second.cellp == nullptr; return moduleNullptr || cellNullptr; } - // Check if the, in the target string, defined variable was found in the previous step bool isFound(const std::pair& pair) { for (auto& entry : pair.second.entries) { if (entry.found == false) { return entry.found; } } return true; } - // Get the fault case for the given key in the configuration map int getMapEntryFaultCase(const std::string& key, size_t index) { const auto& map = V3Control::getHookInsCfg(); const auto insCfg = map.find(key); @@ -641,8 +558,6 @@ class HookInsFunc final : public VNVisitor { return -1; } } - // Get the callback function name from the hook-insertion for the given key in the - // configuration map string getMapEntryFunction(const std::string& key, size_t index) { const auto& map = V3Control::getHookInsCfg(); const auto insCfg = map.find(key); @@ -654,6 +569,7 @@ class HookInsFunc final : public VNVisitor { return ""; } } + // Remove "" from string from ->name() string cleanString(const string& str) { if (str.size() >= 2 && str.front() == '"' && str.back() == '"') { return str.substr(1, str.size() - 2); @@ -661,7 +577,6 @@ class HookInsFunc final : public VNVisitor { return ""; } } - // Set the done flag for the given module node pointer in the configuraiton map void setDone(AstModule* nodep) { auto& insCfg = V3Control::getHookInsCfg(); for (auto& pair : insCfg) { @@ -673,10 +588,8 @@ class HookInsFunc final : public VNVisitor { m_assignNode = true; VDirection dir = m_orig_varp->direction(); if (dir == VDirection::INPUT || dir == VDirection::NONE) { - // Hier muss was mit dem rhsp gemacht werden AstNodeExpr* rhsp = nodep->rhsp(); if (rhsp->type() != VNType::ParseRef) { - // Muss ich hier loopen? for (AstNode* n = rhsp->op1p(); n; n = n->nextp()) { if (n->type() == VNType::ParseRef && n->name() == m_orig_varp->name()) { n->name(m_tmp_varp->name()); @@ -741,10 +654,6 @@ class HookInsFunc final : public VNVisitor { } // Visitors - //---------------------------------------------------------------------------------- - - //ASTNETLIST VISITOR FUNCTION: - //Loop over map entries for module nodes and add them to the tree void visit(AstNetlist* nodep) override { const auto& insCfg = V3Control::getHookInsCfg(); for (const auto& pair : insCfg) { @@ -762,37 +671,18 @@ class HookInsFunc final : public VNVisitor { } } } - - //ASTMODULE VISITOR FUNCTION: - //This function is called for each module node in the netlist. - //It checks if the module node is part of the hook-insertion configuration map. - //Depending on the type of the module node (Hook-inserted, Top, Pointing, or Original), - //it performs different actions: - // - If the module is an hook-inserted module entry and has not been done, it creates a new - //task for the hook-insertion function, adds the temporary variable, and creates a task - //reference to the callback function. - // - If the module is a pointing module or a top module and has no multiple cellps, it - // checks - //the cell for the target key and counts the pins. This pin count is used in the CELL VISITOR - //FUNCTION to set a siutable pin number for the HOOKINS parameter. Look there fore further - //information. - // - If the module is a pointing module and has multiple cellps, it creates a begin block - // with - //a conditional statement to select between the hook-inserted and original cell. - // Additionally like in the previous case, the pin count is used to set a suitable pin - //number for the HOOKINS parameter.\ Since the cell which need to be edited are located not - //in the original module, but in the pointing/top module, the current_module_cell_check - //variable is set to the module visited by the function and fulfilling this condition. void visit(AstModule* nodep) override { const auto& insCfg = V3Control::getHookInsCfg().find(m_targetKey); const HookInsertTarget& target = insCfg->second; const auto& entries = target.entries; + // Insert nodes for hooks into module for each defined target for (m_targetIndex = 0; m_targetIndex < entries.size(); ++m_targetIndex) { m_tmp_varp = getMapEntryInsVar(m_targetKey, m_targetIndex); m_orig_varp = getMapEntryVar(m_targetKey, m_targetIndex); m_task_name = getMapEntryFunction(m_targetKey, m_targetIndex); if (isInsModEntry(nodep, m_targetKey) && !isDone(nodep)) { m_current_module = nodep; + // Add DPI function/task if not already present for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { if (VN_IS(n, Task) && n->name() == m_task_name) { m_taskp = VN_CAST(n, Task); @@ -821,13 +711,14 @@ class HookInsFunc final : public VNVisitor { nodep->addStmtsp(m_taskp); } } + // Prepare and add faulty variable if (m_orig_varp->direction() == VDirection::INPUT) { m_tmp_varp->varType(VVarType::VAR); m_tmp_varp->direction(VDirection::NONE); m_tmp_varp->trace(true); } nodep->addStmtsp(m_tmp_varp); - // Pruefung einbauen ob das schon passiert ist. + // Add trigger if not already present if (m_dpi_trigger == nullptr) { m_dpi_trigger = new AstVar{ nodep->fileline(), VVarType::VAR, "dpi_trigger", VFlagChildDType{}, @@ -840,6 +731,7 @@ class HookInsFunc final : public VNVisitor { nodep->fileline(), new AstBegin{nodep->fileline(), "", m_loopp, false}}; nodep->addStmtsp(initialp); } + // Add taks/function ref depending on taks/func added & create always block if (m_taskp != nullptr) { m_taskrefp = new AstTaskRef{ nodep->fileline(), m_task_name, @@ -860,6 +752,7 @@ class HookInsFunc final : public VNVisitor { nodep->addStmtsp(alwaysp); } if (m_targetIndex == entries.size() - 1) { setDone(nodep); } + // Get pin number for cell edits for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { if (VN_IS(n, Port)) { m_pinnum = VN_CAST(n, Port)->pinNum(); } } @@ -875,6 +768,8 @@ class HookInsFunc final : public VNVisitor { AstCell* insCellp = getMapEntryCell(m_targetKey)->cloneTree(false); insCellp->modp(getMapEntryInsModule(m_targetKey)); for (AstNode* n = insCellp->pinsp(); n; n = n->nextp()) { m_pinnum++; } + // Add logic for deciding between original and hook-inserted module + // depending on the value of HOOKINS parameter bool addedInitGenIf = false; bool breakOuter = false; std::string condValue = ""; @@ -915,6 +810,7 @@ class HookInsFunc final : public VNVisitor { } if (breakOuter) { break; } } + // If no GenIf for HOOKINS added yet, add one if (!addedInitGenIf) { AstGenIf* genifp = new AstGenIf{ nodep->fileline(), @@ -944,16 +840,8 @@ class HookInsFunc final : public VNVisitor { m_loopp = nullptr; m_targetIndex = 0; } - - //ASTPORT VISITOR FUNCTION: - //When the target variable is an ouput port, this function is called. - //If no port is added yet, two new ports are added to the current module. - //This enabled the hook-insertion of the ouput port and link this hook-inserted port to the - //modules reading from the original port. The idea behind this function is to set the - //hook-inserted port on the position of the original port in the module and move the original - //port to another pin number. This should ensure the linking over the name and the port - //position in the module should work. void visit(AstPort* nodep) override { + // Replace original port with tmp port; keep original port for to be sure if (m_current_module != nullptr && m_orig_varp->direction() == VDirection::OUTPUT && nodep->name() == m_orig_varp->name() && !m_addedport) { m_orig_portp = nodep->cloneTree(false); @@ -966,20 +854,6 @@ class HookInsFunc final : public VNVisitor { m_addedport = true; } } - - //ASTCELL VISITOR FUNCTION: - //This function visits the cell nodes in the module pointing to the hook-inserted module. - //Depending if hasMultiple is set for the target key, two different actions are performed: - // - If hasMultiple is false, the cell is modified to link to the hook-inserted module and - // the - //children are iterated. This ensures that the hook-inserted mopdule is used in the cell. Also - //if the original variable is an output variable, the children of this cell nodes are visited - //by the ASTPIN VISITOR FUNCTION. - // - If hasMultiple is true, the cell is unlinked from the back and deleted. - // This ensures that the cell is not used anymore in the module, and the conditional - //statment deciding between the hook-inserted and the original cell can be created/used. A - //third action is performed if the variable beeing hook-inserted is an ouput variable. In this - //case the children of this cell nodes are visited by the ASTPIN VISITOR FUNCTION. void visit(AstCell* nodep) override { bool nodeHasName = false; bool nodeHasCorrectBackp = false; @@ -987,11 +861,15 @@ class HookInsFunc final : public VNVisitor { nodeHasName = (nodep->name() == getMapEntryCell(m_targetKey)->name()); nodeHasCorrectBackp = (nodep->backp()->type() != VNType::GenBlock); isCorrectMultCell = nodeHasName && nodeHasCorrectBackp; + // Edit cell reference depending on situation if (m_current_module_cell_check != nullptr && !hasMultiple(m_targetKey) && nodeHasName) { + // Not multiple cells refer to target module; edit module reference nodep->modp(getMapEntryInsModule(m_targetKey)); if (m_orig_varp->direction() == VDirection::OUTPUT) { iterateChildren(nodep); } } else if (m_current_module_cell_check != nullptr && hasMultiple(m_targetKey) && isCorrectMultCell) { + // Multiple cells link to target module; + // delete original cell add logic with module visitor nodep->unlinkFrBack(); nodep->deleteTree(); } else if (m_insGenBlock != nullptr && nodep->modp() == getMapEntryInsModule(m_targetKey) @@ -1001,11 +879,6 @@ class HookInsFunc final : public VNVisitor { iterateChildren(nodep); } } - - //ASTPIN VISITOR FUNCTION: - //The function is used to change the pin name of the original variable to the hook-inserted - //variable name. This is done to ensure that the pin is correctly linked to the hook-inserted - //variable in the cell. void visit(AstPin* nodep) override { if (nodep->name() == m_orig_varp->name() && m_orig_varp->direction() == VDirection::INPUT) { @@ -1014,9 +887,6 @@ class HookInsFunc final : public VNVisitor { nodep->name(m_tmp_varp->name()); } } - - //ASTTASK VISITOR FUNCTION: - //The function is used to further specify the task node created at the module visitor. void visit(AstTask* nodep) override { if (m_addedTask == false && nodep == m_taskp && m_current_module != nullptr) { AstVar* insID = nullptr; @@ -1041,9 +911,6 @@ class HookInsFunc final : public VNVisitor { nodep->addStmtsp(tmp_var_task); } } - - //ASTFUNC VISITOR FUNCITON: - //The function is used to further specify the function node created at the module visitor. void visit(AstFunc* nodep) override { if (m_addedFunc == false && nodep == m_funcp && m_current_module != nullptr) { AstVar* insID = nullptr; @@ -1067,11 +934,8 @@ class HookInsFunc final : public VNVisitor { nodep->addStmtsp(var_x_func); } } - - //ASTLOOP VISITOR FUNCTION: - //The function is used to add the logic for the dpi trigger, that triggers the DPI function at - //the smallest possible time intervals. void visit(AstLoop* nodep) override { + // Add initial block for DPI trigger variable if (nodep == m_loopp && m_current_module != nullptr) { AstParseRef* initialParseRefrhsp = new AstParseRef{nodep->fileline(), m_dpi_trigger->name()}; @@ -1088,11 +952,8 @@ class HookInsFunc final : public VNVisitor { nodep->addContsp(initialBeginp); } } - - //ASTALWAYS VISITOR FUNCTION: - //The function is used to add the task reference node to the always node and further specify - //the always node. void visit(AstAlways* nodep) override { + // Add task reference in the new always block if (nodep == m_alwaysp && m_current_module != nullptr) { AstBegin* newBegin = nullptr; @@ -1104,15 +965,12 @@ class HookInsFunc final : public VNVisitor { } iterateChildren(nodep); } - void visit(AstVar* nodep) override { + // Store hooked var in hooked module to ensure correct references if (m_current_module != nullptr && nodep->name() == m_orig_varp->name()) { m_orig_varp_insMod = nodep; } } - - //ASTTASKREF VISITOR FUNCTION: - //The function is used to further specify the task reference node called by the always node. void visit(AstTaskRef* nodep) override { if (nodep == m_taskrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; @@ -1130,10 +988,6 @@ class HookInsFunc final : public VNVisitor { m_orig_varp_insMod = nullptr; } } - - //ASTFUNCREF VISITOR FUNCTION: - //The function is used to further specify the function reference node called by the assignw - //node void visit(AstFuncRef* nodep) override { if (nodep == m_funcrefp && m_current_module != nullptr) { AstConst* constp_id = nullptr; @@ -1155,28 +1009,12 @@ class HookInsFunc final : public VNVisitor { m_funcrefp = nullptr; } } - - //ASTASSIGNW VISITOR FUNCTION: - //Sets the m_assignw flag to true if the current module is not null. - //Necessary for the AstParseRef visitor function to determine if the current node is part of an - //assignment. - void visit(AstAssignW* nodep) override { insAssigns(nodep); } - void visit(AstAssign* nodep) override { insAssigns(nodep); } - void visit(AstAssignDly* nodep) override { insAssigns(nodep); } - void visit(AstAssignForce* nodep) override { insAssigns(nodep); } - - //ASTPARSE REF VISITOR FUNCTION: - //The function is used to change the parseref nodes to link to the hook-inserted variable - //instead of the original variable. Depending on the direction of the original variable, - //different actions are performed: - // - If the original variable is not an output variable and the assignment is true, the - //parseref node is changed to link to the hook-inserted variable. This ensures that the - //hook-inserted variable is used in the assignment. - // - If the original variable is an input variable, every parseref node is changed to link - // to - //the hook-inserted variable. This ensures that the hook-inserted variable is used as the new - //input. + void visit(AstAssignW* nodep) override { insAssigns(nodep); } // Edit assigns if needed + void visit(AstAssign* nodep) override { insAssigns(nodep); } // Edit assigns if needed + void visit(AstAssignDly* nodep) override { insAssigns(nodep); } // Edit assigns if needed + void visit(AstAssignForce* nodep) override { insAssigns(nodep); } // Edit assigns if needed void visit(AstParseRef* nodep) override { + // Replace original var with tmp var in non-assign nodes if (m_current_module != nullptr && m_orig_varp != nullptr && m_orig_varp->direction() != VDirection::OUTPUT) { if (nodep->name() == m_orig_varp->name() && !m_assignNode) { @@ -1197,15 +1035,12 @@ public: //################################################################################## // Hook-insertion class functions -// Function to find hook-insertion targets and additional information for the hook-insertion -// process void V3InsertHook::findTargets(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { HookInsTargetFndr{nodep}; } V3Global::dumpCheckGlobalTree("hookInsertFinder", 0, dumpTreeEitherLevel() >= 3); } -// Function for the actual hook-insertion process void V3InsertHook::insertHooks(AstNetlist* nodep) { UINFO(2, __FUNCTION__ << ": " << endl); { HookInsFunc{nodep}; } From 316caeb4cb5106927538fa7e98cec5c1e1333248 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jonathan=20Schr=C3=B6ter?= Date: Wed, 3 Dec 2025 14:24:19 +0100 Subject: [PATCH 20/20] Revert "feat: Add V3DumpSignals-Pass" This reverts commit faf67f45f69d26fc328fbefdb6fc00e13e51e26d. --- src/CMakeLists.txt | 2 - src/Makefile_obj.in | 1 - src/V3DumpSignals.cpp | 118 ------------------------------------------ src/V3DumpSignals.h | 32 ------------ src/V3Options.cpp | 1 - src/V3Options.h | 2 - src/Verilator.cpp | 3 -- 7 files changed, 159 deletions(-) delete mode 100644 src/V3DumpSignals.cpp delete mode 100644 src/V3DumpSignals.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index fceb8d48d..2e35d21ce 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -84,7 +84,6 @@ set(HEADERS V3DfgPeepholePatterns.h V3DfgVertices.h V3DiagSarif.h - V3DumpSignals.h V3DupFinder.h V3EmitC.h V3EmitCBase.h @@ -252,7 +251,6 @@ set(COMMON_SOURCES V3DfgRegularize.cpp V3DfgSynthesize.cpp V3DiagSarif.cpp - V3DumpSignals.cpp V3DupFinder.cpp V3EmitCBase.cpp V3EmitCConstPool.cpp diff --git a/src/Makefile_obj.in b/src/Makefile_obj.in index 06df7b87f..69b8b0280 100644 --- a/src/Makefile_obj.in +++ b/src/Makefile_obj.in @@ -269,7 +269,6 @@ RAW_OBJS_PCH_ASTNOMT = \ V3DfgRegularize.o \ V3DfgSynthesize.o \ V3DiagSarif.o \ - V3DumpSignals.o \ V3DupFinder.o \ V3EmitCMain.o \ V3EmitCMake.o \ diff --git a/src/V3DumpSignals.cpp b/src/V3DumpSignals.cpp deleted file mode 100644 index 50d9e129e..000000000 --- a/src/V3DumpSignals.cpp +++ /dev/null @@ -1,118 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: -// -// 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 "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT - -#include "V3DumpSignals.h" - -#include "V3Global.h" - -#include -#include -#include - -VL_DEFINE_DEBUG_FUNCTIONS; -V3Global v3global; - -class DumpSignals final : public VNVisitor { - bool m_firstModuleNode = true; - bool m_foundCell = false; - string m_currHier; - std::ofstream m_signalFile; - - // Methods - void processVar(AstVar* varp) { - if (varp->basicp() && varp->basicp()->name() != "") { - bool hasRangep = varp->basicp()->rangep() != nullptr; - if (hasRangep) { - std::string varHier - = m_currHier + varp->name() + " : Type[" + varp->basicp()->name() + "] Width[" - + std::to_string(varp->basicp()->rangep()->elementsConst()) + "]"; - m_signalFile << varHier << "\n"; - } else { - if (varp->basicp()->implicit()) { - // Since Var is implicit set the width to 1 like in V3Width.cpp in the AstVar - // visitor - std::string varHier = m_currHier + varp->name() + " : Type[" - + varp->basicp()->name() + "] Width[" + std::to_string(1) - + "]"; - m_signalFile << varHier << "\n"; - } else { - std::string varHier = m_currHier + varp->name() + " : Type[" - + varp->basicp()->name() + "] Width[" - + std::to_string(varp->basicp()->width()) + "]"; - m_signalFile << varHier << "\n"; - } - } - } - } - - void processChildrenNode(AstNode* nodep) { - for (AstNode* n = nodep->op2p(); n; n = n->nextp()) { - if (VN_IS(n, Var)) { - AstVar* varp = VN_AS(n, Var); - if (!varp->isParam() && !varp->isGenVar() && !varp->isIfaceRef() - && !varp->isIfaceParent()) { - processVar(varp); - } - } else if (VN_IS(n, Cell)) { - if (VN_IS(VN_AS(n, Cell)->modp(), Module)) { - m_foundCell = true; - std::string oldHier = m_currHier; - m_currHier += n->name() + "."; - diveIntoCellModp(VN_AS(n, Cell)->modp()); - m_currHier = oldHier; - } - } - } - } - - void diveIntoCellModp(AstNodeModule* modp) { processChildrenNode(modp); } - - // VISITORS - void visit(AstModule* nodep) override { - if (m_firstModuleNode) { - m_currHier = nodep->name() + "."; - processChildrenNode(nodep); - m_firstModuleNode = false; - } - } - - //----------------- - void visit(AstNode* nodep) override { iterateChildren(nodep); } - -public: - explicit DumpSignals(AstNetlist* nodep) { - std::string filePath = v3global.opt.hierTopDataDir() + "/signalDump.log"; - m_signalFile.open(filePath); - iterate(nodep); - } - - ~DumpSignals() override { - if (m_signalFile.is_open()) { m_signalFile.close(); } - } -}; - -//################################################################################## -// DumpSignals class functions - -void V3DumpSignals::dumpSignals(AstNetlist* nodep) { - UINFO(2, __FUNCTION__ << ": " << endl); - { DumpSignals{nodep}; } - V3Global::dumpCheckGlobalTree("dumpSignals", 0, dumpTreeEitherLevel() >= 3); -} diff --git a/src/V3DumpSignals.h b/src/V3DumpSignals.h deleted file mode 100644 index baf24f06c..000000000 --- a/src/V3DumpSignals.h +++ /dev/null @@ -1,32 +0,0 @@ -// -*- mode: C++; c-file-style: "cc-mode" -*- -//************************************************************************* -// DESCRIPTION: Verilator: -// -// 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 -// -//************************************************************************* - -#ifndef VERILATOR_V3DUMPSIGNALS_H_ -#define VERILATOR_V3DUMPSIGNALS_H_ - -#include "config_build.h" -#include "verilatedos.h" - -class AstNetlist; - -//========================================================================= - -class V3DumpSignals final { -public: - static void dumpSignals(AstNetlist* nodep) VL_MT_DISABLED; -}; - -#endif // Guard diff --git a/src/V3Options.cpp b/src/V3Options.cpp index 8c6808d97..ca231e5b8 100644 --- a/src/V3Options.cpp +++ b/src/V3Options.cpp @@ -1947,7 +1947,6 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, }); DECL_OPTION("-insert-hook", OnOff, &m_insertHook); - DECL_OPTION("-dump-signals", OnOff, &m_dumpSignals); parser.finalize(); diff --git a/src/V3Options.h b/src/V3Options.h index 7603a3788..dd51202ed 100644 --- a/src/V3Options.h +++ b/src/V3Options.h @@ -250,7 +250,6 @@ private: bool m_decorationNodes = false; // main switch: --decoration=nodes bool m_diagnosticsSarif = false; // main switch: --diagnostics-sarif bool m_dpiHdrOnly = false; // main switch: --dpi-hdr-only - bool m_dumpSignals = false; // main switch: --dump-signals bool m_emitAccessors = false; // main switch: --emit-accessors bool m_exe = false; // main switch: --exe bool m_flatten = false; // main switch: --flatten @@ -530,7 +529,6 @@ public: bool diagnosticsSarif() const VL_MT_SAFE { return m_diagnosticsSarif; } bool dpiHdrOnly() const { return m_dpiHdrOnly; } bool dumpDefines() const { return m_dumpLevel.count("defines") && m_dumpLevel.at("defines"); } - bool dumpSignals() const { return m_dumpSignals; } bool dumpTreeDot() const { return m_dumpLevel.count("tree-dot") && m_dumpLevel.at("tree-dot"); } diff --git a/src/Verilator.cpp b/src/Verilator.cpp index 232a2b67b..e978ca4c8 100644 --- a/src/Verilator.cpp +++ b/src/Verilator.cpp @@ -45,7 +45,6 @@ #include "V3Descope.h" #include "V3DfgOptimizer.h" #include "V3DiagSarif.h" -#include "V3DumpSignals.h" #include "V3EmitC.h" #include "V3EmitCMain.h" #include "V3EmitCMake.h" @@ -154,8 +153,6 @@ static void process() { v3Global.vlExit(0); } - if (v3Global.opt.dumpSignals()) { V3DumpSignals::dumpSignals(v3Global.rootp()); } - // Hook-insert design with the configurations given in .vlt file if (v3Global.opt.insertHook()) { v3Global.dpi(true);