Refactoring before renaming tests

Signed-off-by: Matthew Ballance <matt.ballance@gmail.com>
This commit is contained in:
Matthew Ballance 2026-02-25 01:16:55 +00:00
parent 43bfd85dc0
commit c749ff09b4
19 changed files with 388 additions and 929 deletions

View File

@ -1,300 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//=============================================================================
//
// Code available from: https://verilator.org
//
// 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-FileCopyrightText: 2026-2026 by Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//=============================================================================
///
/// \file
/// \brief Verilated functional coverage support header
///
/// This file provides runtime support for SystemVerilog functional coverage
/// constructs (covergroups, coverpoints, bins, cross coverage).
///
//=============================================================================
#ifndef VERILATOR_VERILATED_FUNCCOV_H_
#define VERILATOR_VERILATED_FUNCCOV_H_
#include "verilatedos.h"
#include "verilated.h"
#include "verilated_cov.h"
#include <map>
#include <string>
#include <vector>
//=============================================================================
// VerilatedCoverBin - Represents a single bin in a coverpoint
class VerilatedCoverBin VL_NOT_FINAL {
private:
std::string m_name; // Bin name
std::string m_rangeStr; // String representation of range (e.g., "0:15")
uint32_t m_count = 0; // Hit count
uint32_t* m_countp = nullptr; // Pointer to counter (for coverage registration)
public:
VerilatedCoverBin(const std::string& name, const std::string& rangeStr)
: m_name{name}
, m_rangeStr{rangeStr}
, m_countp{&m_count} {}
virtual ~VerilatedCoverBin() = default;
// Accessors
const std::string& name() const { return m_name; }
const std::string& rangeStr() const { return m_rangeStr; }
uint32_t count() const { return m_count; }
uint32_t* countp() { return m_countp; }
// Increment hit count
void hit() { ++m_count; }
// Check if value matches this bin (to be overridden by specific bin types)
virtual bool matches(uint64_t value) const { return false; }
};
//=============================================================================
// VerilatedCoverRangeBin - Bin that matches a value range
class VerilatedCoverRangeBin final : public VerilatedCoverBin {
private:
uint64_t m_min;
uint64_t m_max;
public:
VerilatedCoverRangeBin(const std::string& name, uint64_t min, uint64_t max)
: VerilatedCoverBin(name, std::to_string(min) + ":" + std::to_string(max))
, m_min{min}
, m_max{max} {}
bool matches(uint64_t value) const override { return value >= m_min && value <= m_max; }
};
//=============================================================================
// VerilatedCoverpoint - Represents a coverage point
class VerilatedCoverpoint VL_NOT_FINAL {
private:
std::string m_name; // Coverpoint name
std::vector<VerilatedCoverBin*> m_bins; // Bins in this coverpoint
bool m_enabled = true; // Coverage collection enabled
public:
explicit VerilatedCoverpoint(const std::string& name)
: m_name{name} {}
~VerilatedCoverpoint() {
for (auto* bin : m_bins) delete bin;
}
// Accessors
const std::string& name() const { return m_name; }
const std::vector<VerilatedCoverBin*>& bins() const { return m_bins; }
bool enabled() const { return m_enabled; }
void enabled(bool flag) { m_enabled = flag; }
// Add a bin to this coverpoint
void addBin(VerilatedCoverBin* binp) { m_bins.push_back(binp); }
// Sample a value and update bin counts
void sample(uint64_t value) {
if (!m_enabled) return;
for (auto* bin : m_bins) {
if (bin->matches(value)) { bin->hit(); }
}
}
// Compute coverage percentage
double getCoverage(uint32_t atLeast = 1) const {
if (m_bins.empty()) return 100.0;
int coveredBins = 0;
for (const auto* bin : m_bins) {
if (bin->count() >= atLeast) ++coveredBins;
}
return (100.0 * coveredBins) / m_bins.size();
}
// Register bins with coverage infrastructure
void registerCoverage(VerilatedCovContext* contextp, const std::string& hier,
const std::string& cgName) {
for (auto* bin : m_bins) {
const std::string fullName = cgName + "." + m_name;
const std::string& binName = bin->name();
const std::string& binRange = bin->rangeStr();
VL_COVER_INSERT(contextp, hier.c_str(), bin->countp(), "type", "coverpoint", "name",
fullName.c_str(), "bin", binName.c_str(), "range", binRange.c_str());
}
}
};
//=============================================================================
// VerilatedCoverCross - Represents cross coverage between coverpoints
class VerilatedCoverCross VL_NOT_FINAL {
private:
std::string m_name; // Cross name
std::vector<VerilatedCoverpoint*> m_coverpoints; // Coverpoints being crossed
std::map<std::string, uint32_t> m_crossBins; // Sparse storage: "<bin1,bin2>" -> count
bool m_enabled = true;
public:
explicit VerilatedCoverCross(const std::string& name)
: m_name{name} {}
// Accessors
const std::string& name() const { return m_name; }
bool enabled() const { return m_enabled; }
void enabled(bool flag) { m_enabled = flag; }
// Add a coverpoint to cross
void addCoverpoint(VerilatedCoverpoint* cpp) { m_coverpoints.push_back(cpp); }
// Sample cross product (to be called after coverpoints are sampled)
void sample(const std::vector<uint64_t>& values) {
if (!m_enabled || values.size() != m_coverpoints.size()) return;
// Build cross bin key from matched bins
std::string key = "<";
bool first = true;
for (size_t i = 0; i < values.size(); ++i) {
// Find which bin matched for this coverpoint
for (const auto* bin : m_coverpoints[i]->bins()) {
if (bin->matches(values[i])) {
if (!first) key += ",";
key += bin->name();
first = false;
break;
}
}
}
key += ">";
// Increment cross bin count
m_crossBins[key]++;
}
// Compute coverage percentage
double getCoverage(uint32_t atLeast = 1) const {
if (m_crossBins.empty()) return 100.0;
int coveredBins = 0;
for (const auto& pair : m_crossBins) {
if (pair.second >= atLeast) ++coveredBins;
}
// Total possible bins is product of coverpoint bin counts
size_t totalBins = 1;
for (const auto* cp : m_coverpoints) { totalBins *= cp->bins().size(); }
return (100.0 * coveredBins) / totalBins;
}
// Register cross bins with coverage infrastructure
void registerCoverage(VerilatedCovContext* contextp, const std::string& hier,
const std::string& cgName) {
// Cross bins are registered dynamically as they're hit
// For now, we'll register them all upfront (can be optimized later)
std::string fullName = cgName + "." + m_name;
for (const auto& pair : m_crossBins) {
// Note: We need a persistent counter, so we use the map value's address
// This is safe because std::map doesn't reallocate on insert
const std::string& binName = pair.first;
VL_COVER_INSERT(contextp, hier.c_str(), const_cast<uint32_t*>(&pair.second), "type",
"cross", "name", fullName.c_str(), "bin", binName.c_str());
}
}
};
//=============================================================================
// VerilatedCovergroup - Represents a covergroup instance
class VerilatedCovergroup VL_NOT_FINAL {
private:
std::string m_name; // Covergroup type name
std::string m_instName; // Instance name
std::vector<VerilatedCoverpoint*> m_coverpoints;
std::vector<VerilatedCoverCross*> m_crosses;
bool m_enabled = true;
// Coverage options
uint32_t m_weight = 1;
uint32_t m_goal = 100;
uint32_t m_atLeast = 1;
std::string m_comment;
public:
explicit VerilatedCovergroup(const std::string& name)
: m_name{name}
, m_instName{name} {}
~VerilatedCovergroup() {
for (auto* cp : m_coverpoints) delete cp;
for (auto* cross : m_crosses) delete cross;
}
// Accessors
const std::string& name() const { return m_name; }
const std::string& instName() const { return m_instName; }
void instName(const std::string& name) { m_instName = name; }
bool enabled() const { return m_enabled; }
// Options
void weight(uint32_t w) { m_weight = w; }
void goal(uint32_t g) { m_goal = g; }
void atLeast(uint32_t a) { m_atLeast = a; }
void comment(const std::string& c) { m_comment = c; }
// Add components
void addCoverpoint(VerilatedCoverpoint* cpp) { m_coverpoints.push_back(cpp); }
void addCross(VerilatedCoverCross* cross) { m_crosses.push_back(cross); }
// Predefined methods per IEEE 1800-2023 Section 19.9
void sample() {
if (!m_enabled) return;
// Sampling is done by generated code calling coverpoint sample() methods
}
void start() { m_enabled = true; }
void stop() { m_enabled = false; }
void set_inst_name(const std::string& name) { m_instName = name; }
// Get type coverage (0-100)
double get_coverage() const {
if (m_coverpoints.empty()) return 100.0;
double totalCov = 0.0;
uint32_t totalWeight = 0;
for (const auto* cp : m_coverpoints) {
totalCov += cp->getCoverage(m_atLeast) * m_weight;
totalWeight += m_weight;
}
for (const auto* cross : m_crosses) {
totalCov += cross->getCoverage(m_atLeast) * m_weight;
totalWeight += m_weight;
}
return totalWeight > 0 ? totalCov / totalWeight : 100.0;
}
// Get instance coverage (same as type coverage for now)
double get_inst_coverage() const { return get_coverage(); }
// Register all coverage points with coverage infrastructure
void registerCoverage(VerilatedCovContext* contextp, const std::string& hier) {
// Register covergroup metadata
// (Will be extended when we add metadata output)
// Register all coverpoints
for (auto* cp : m_coverpoints) { cp->registerCoverage(contextp, hier, m_name); }
// Register all crosses
for (auto* cross : m_crosses) { cross->registerCoverage(contextp, hier, m_name); }
}
};
#endif // guard

View File

@ -70,8 +70,7 @@ set(HEADERS
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3CoverageFunctional.h
V3AstNodeFuncCov.h
V3Covergroup.h
V3Dead.h
V3DebugBisect.h
V3Delayed.h
@ -239,8 +238,7 @@ set(COMMON_SOURCES
V3Const__gen.cpp
V3Coverage.cpp
V3CoverageJoin.cpp
V3CoverageFunctional.cpp
V3AstNodeFuncCov.cpp
V3Covergroup.cpp
V3Dead.cpp
V3Delayed.cpp
V3Depth.cpp
@ -405,7 +403,7 @@ add_custom_command(
ARGS
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef
V3AstNodeExpr.h --astdef V3AstNodeOther.h --astdef V3AstNodeStmt.h
--astdef V3AstNodeFuncCov.h --dfgdef V3DfgVertices.h --classes
--dfgdef V3DfgVertices.h --classes
)
list(
APPEND GENERATED_FILES
@ -517,7 +515,7 @@ foreach(astgen_name ${ASTGENERATED_NAMES})
ARGS
${ASTGEN} -I "${srcdir}" --astdef V3AstNodeDType.h --astdef
V3AstNodeExpr.h --astdef V3AstNodeOther.h --astdef V3AstNodeStmt.h
--astdef V3AstNodeFuncCov.h --dfgdef V3DfgVertices.h
--dfgdef V3DfgVertices.h
${astgen_name}.cpp
)
list(APPEND GENERATED_FILES ${astgen_name}__gen.cpp)

View File

@ -207,7 +207,6 @@ RAW_OBJS = \
RAW_OBJS_PCH_ASTMT = \
V3Ast.o \
V3AstNodeFuncCov.o \
V3AstNodes.o \
V3Broken.o \
V3Control.o \
@ -250,7 +249,7 @@ RAW_OBJS_PCH_ASTNOMT = \
V3Combine.o \
V3Common.o \
V3Coverage.o \
V3CoverageFunctional.o \
V3Covergroup.o \
V3CoverageJoin.o \
V3Dead.o \
V3Delayed.o \
@ -362,7 +361,6 @@ NON_STANDALONE_HEADERS = \
V3AstInlines.h \
V3AstNodeDType.h \
V3AstNodeExpr.h \
V3AstNodeFuncCov.h \
V3AstNodeOther.h \
V3AstNodeStmt.h \
V3DebugBisect.h \
@ -373,7 +371,6 @@ NON_STANDALONE_HEADERS = \
AST_DEFS := \
V3AstNodeDType.h \
V3AstNodeExpr.h \
V3AstNodeFuncCov.h \
V3AstNodeOther.h \
V3AstNodeStmt.h \

View File

@ -634,7 +634,7 @@ class CovergroupSamplingVisitor final : public VNVisitor {
// Helper to get the clocking event from a covergroup class
AstSenTree* getCovergroupEvent(AstClass* classp) {
// The AstCovergroup (holding the SenTree) was left in membersp by V3CoverageFunctional
// The AstCovergroup (holding the SenTree) was left in membersp by V3Covergroup
for (AstNode* memberp = classp->membersp(); memberp; memberp = memberp->nextp()) {
if (AstCovergroup* const cgp = VN_CAST(memberp, Covergroup)) {
if (cgp->eventp()) return cgp->eventp();

View File

@ -1583,7 +1583,6 @@ AstNode* VNVisitor::iterateSubtreeReturnEdits(AstNode* nodep) {
#include "V3AstNodeOther.h"
#include "V3AstNodeExpr.h"
#include "V3AstNodeStmt.h"
#include "V3AstNodeFuncCov.h"
// clang-format on
// Inline function definitions need to go last

View File

@ -1,155 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode implementation for functional coverage nodes
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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-FileCopyrightText: 2026-2026 by Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#include "V3PchAstMT.h"
#include "V3AstNodeFuncCov.h"
//######################################################################
// Dump methods
void AstCovergroup::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
if (m_isClass) str << " [class]";
}
void AstCovergroup::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(name());
if (m_isClass) str << ", \"isClass\": true";
}
void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverBin::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name << " ";
switch (m_type) {
case VCoverBinsType::USER: str << "user"; break;
case VCoverBinsType::ARRAY: str << "array"; break;
case VCoverBinsType::AUTO: str << "auto"; break;
case VCoverBinsType::BINS_IGNORE: str << "ignore"; break;
case VCoverBinsType::BINS_ILLEGAL: str << "illegal"; break;
case VCoverBinsType::DEFAULT: str << "default"; break;
case VCoverBinsType::BINS_WILDCARD: str << "wildcard"; break;
case VCoverBinsType::TRANSITION: str << "transition"; break;
}
if (m_isArray) str << "[]";
}
void AstCoverBin::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
str << ", \"binsType\": ";
switch (m_type) {
case VCoverBinsType::USER: str << "\"user\""; break;
case VCoverBinsType::ARRAY: str << "\"array\""; break;
case VCoverBinsType::AUTO: str << "\"auto\""; break;
case VCoverBinsType::BINS_IGNORE: str << "\"ignore\""; break;
case VCoverBinsType::BINS_ILLEGAL: str << "\"illegal\""; break;
case VCoverBinsType::DEFAULT: str << "\"default\""; break;
case VCoverBinsType::BINS_WILDCARD: str << "\"wildcard\""; break;
case VCoverBinsType::TRANSITION: str << "\"transition\""; break;
}
if (m_isArray) str << ", \"isArray\": true";
}
void AstCoverTransItem::dump(std::ostream& str) const {
this->AstNode::dump(str);
switch (m_repType) {
case VTransRepType::NONE: break;
case VTransRepType::CONSEC: str << " [*]"; break;
case VTransRepType::GOTO: str << " [->]"; break;
case VTransRepType::NONCONS: str << " [=]"; break;
}
}
void AstCoverTransItem::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
if (m_repType != VTransRepType::NONE) {
str << ", \"repType\": ";
switch (m_repType) {
case VTransRepType::NONE: break;
case VTransRepType::CONSEC: str << "\"consec\""; break;
case VTransRepType::GOTO: str << "\"goto\""; break;
case VTransRepType::NONCONS: str << "\"noncons\""; break;
}
}
}
void AstCoverTransSet::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " trans_set";
}
void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverCrossBins::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
}
void AstCoverCrossBins::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
void AstCoverOption::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " ";
switch (m_type) {
case VCoverOptionType::WEIGHT: str << "weight"; break;
case VCoverOptionType::GOAL: str << "goal"; break;
case VCoverOptionType::AT_LEAST: str << "at_least"; break;
case VCoverOptionType::AUTO_BIN_MAX: str << "auto_bin_max"; break;
case VCoverOptionType::PER_INSTANCE: str << "per_instance"; break;
case VCoverOptionType::COMMENT: str << "comment"; break;
}
}
void AstCoverOption::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"optionType\": ";
switch (m_type) {
case VCoverOptionType::WEIGHT: str << "\"weight\""; break;
case VCoverOptionType::GOAL: str << "\"goal\""; break;
case VCoverOptionType::AT_LEAST: str << "\"at_least\""; break;
case VCoverOptionType::AUTO_BIN_MAX: str << "\"auto_bin_max\""; break;
case VCoverOptionType::PER_INSTANCE: str << "\"per_instance\""; break;
case VCoverOptionType::COMMENT: str << "\"comment\""; break;
}
}
void AstCoverpointRef::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
}
void AstCoverpointRef::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
void AstCoverSelectExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstCoverSelectExpr::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }

View File

@ -1,289 +0,0 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: AstNode sub-types for functional coverage
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// 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-FileCopyrightText: 2026-2026 by Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
//
// This file contains AST nodes for SystemVerilog functional coverage
// (IEEE 1800-2023 Section 19)
//
//*************************************************************************
#ifndef VERILATOR_V3ASTNODEFUNCCOV_H_
#define VERILATOR_V3ASTNODEFUNCCOV_H_
#ifndef VERILATOR_V3AST_H_
#error "Use V3Ast.h as the include"
#include "V3Ast.h"
#define VL_NOT_FINAL
#endif
//######################################################################
// Enumerations
enum class VCoverBinsType : uint8_t {
USER,
ARRAY,
AUTO,
BINS_IGNORE, // Renamed to avoid Windows macro conflict
BINS_ILLEGAL, // Renamed to avoid Windows macro conflict
DEFAULT,
BINS_WILDCARD, // Renamed to avoid Windows macro conflict
TRANSITION
};
enum class VCoverOptionType : uint8_t {
WEIGHT,
GOAL,
AT_LEAST,
AUTO_BIN_MAX,
PER_INSTANCE,
COMMENT
};
enum class VTransRepType : uint8_t {
NONE, // No repetition
CONSEC, // Consecutive repetition [*]
GOTO, // Goto repetition [->]
NONCONS // Nonconsecutive repetition [=]
};
//######################################################################
// Base classes
class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode {
protected:
string m_name;
public:
AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name)
: AstNode{t, fl}
, m_name{name} {}
ASTGEN_MEMBERS_AstNodeFuncCovItem;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; }
bool maybePointedTo() const override { return true; }
};
//######################################################################
// Concrete nodes - ORDER MATTERS FOR ASTGEN!
// Must be in order: CoverBin, CoverCrossBins, CoverOption, CoverSelectExpr,
// CoverTransItem, CoverTransSet, Covergroup, CoverpointRef, CoverCross,
// Coverpoint
// Forward declarations for types used in constructors
class AstCoverTransSet;
class AstCoverSelectExpr;
class AstCoverBin final : public AstNode {
// @astgen op1 := rangesp : List[AstNode]
// @astgen op2 := iffp : Optional[AstNodeExpr]
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
// @astgen op4 := transp : List[AstCoverTransSet]
string m_name;
VCoverBinsType m_type;
bool m_isArray = false;
public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isWildcard ? VCoverBinsType::BINS_WILDCARD
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: VCoverBinsType::USER))} {
if (rangesp) addRangesp(rangesp);
}
// Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{VCoverBinsType::AUTO}
, m_isArray{true} {
this->arraySizep(arraySizep);
}
// Constructor for default bins (catch-all)
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{type} {
// DEFAULT bins have no ranges - they catch everything not in other bins
}
// Constructor for transition bins
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp, bool isIgnore,
bool isIllegal, bool isArrayBin = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE : VCoverBinsType::TRANSITION)}
, m_isArray{isArrayBin} {
if (transp) addTransp(transp);
}
ASTGEN_MEMBERS_AstCoverBin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
VCoverBinsType binsType() const { return m_type; }
bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; }
};
class AstCoverCrossBins final : public AstNode {
// @astgen op1 := selectp : Optional[AstCoverSelectExpr]
string m_name;
public:
AstCoverCrossBins(FileLine* fl, const string& name, AstCoverSelectExpr* selectp)
: ASTGEN_SUPER_CoverCrossBins(fl)
, m_name{name} {
this->selectp(selectp);
}
ASTGEN_MEMBERS_AstCoverCrossBins;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
};
class AstCoverOption final : public AstNode {
// @astgen op1 := valuep : AstNodeExpr
VCoverOptionType m_type;
public:
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl)
, m_type{type} {
this->valuep(valuep);
}
ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VCoverOptionType optionType() const { return m_type; }
};
class AstCoverSelectExpr final : public AstNode {
// @astgen op1 := exprp : AstNodeExpr
public:
AstCoverSelectExpr(FileLine* fl, AstNodeExpr* exprp)
: ASTGEN_SUPER_CoverSelectExpr(fl) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverSelectExpr;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
class AstCoverTransItem final : public AstNode {
// @astgen op1 := valuesp : List[AstNode] // Range list (values or ranges)
// @astgen op2 := repMinp : Optional[AstNodeExpr] // Repetition min count (for [*], [->], [=])
// @astgen op3 := repMaxp : Optional[AstNodeExpr] // Repetition max count (for ranges)
VTransRepType m_repType;
public:
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
: ASTGEN_SUPER_CoverTransItem(fl)
, m_repType{repType} {
if (valuesp) addValuesp(valuesp);
}
ASTGEN_MEMBERS_AstCoverTransItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VTransRepType repType() const { return m_repType; }
};
// Represents a transition set: value1 => value2 => value3
class AstCoverTransSet final : public AstNode {
// @astgen op1 := itemsp : List[AstCoverTransItem]
public:
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
: ASTGEN_SUPER_CoverTransSet(fl) {
if (itemsp) addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverTransSet;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCovergroup final : public AstNode {
// @astgen op1 := argsp : List[AstVar]
// @astgen op2 := membersp : List[AstNode]
// @astgen op3 := eventp : Optional[AstSenTree]
string m_name;
bool m_isClass = false;
public:
AstCovergroup(FileLine* fl, const string& name, AstNode* membersp, AstSenTree* eventp)
: ASTGEN_SUPER_Covergroup(fl)
, m_name{name} {
if (membersp) addMembersp(membersp);
this->eventp(eventp);
}
ASTGEN_MEMBERS_AstCovergroup;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isClass() const { return m_isClass; }
void isClass(bool flag) { m_isClass = flag; }
bool maybePointedTo() const override { return true; }
};
class AstCoverpointRef final : public AstNode {
// @astgen ptr := m_coverpointp : Optional[AstCoverpoint]
string m_name;
public:
AstCoverpointRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverpointRef(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstCoverpointRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
AstCoverpoint* coverpointp() const { return m_coverpointp; }
void coverpointp(AstCoverpoint* nodep) { m_coverpointp = nodep; }
};
class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := binsp : List[AstCoverCrossBins]
// @astgen op3 := optionsp : List[AstCoverOption]
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
: ASTGEN_SUPER_CoverCross(fl, name) {
if (itemsp) addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverCross;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverpoint final : public AstNodeFuncCovItem {
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := binsp : List[AstCoverBin]
// @astgen op3 := iffp : Optional[AstNodeExpr]
// @astgen op4 := optionsp : List[AstCoverOption]
public:
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp)
: ASTGEN_SUPER_Coverpoint(fl, name) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverpoint;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
//######################################################################
#endif // Guard

View File

@ -253,6 +253,20 @@ public:
string name() const override VL_MT_STABLE { return m_name; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
class AstNodeFuncCovItem VL_NOT_FINAL : public AstNode {
// Base class for functional coverage items (coverpoints, crosses)
protected:
string m_name;
public:
AstNodeFuncCovItem(VNType t, FileLine* fl, const string& name)
: AstNode{t, fl}
, m_name{name} {}
ASTGEN_MEMBERS_AstNodeFuncCovItem;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& flag) override { m_name = flag; }
bool maybePointedTo() const override { return true; }
};
class AstNodeGen VL_NOT_FINAL : public AstNode {
// Generate construct
public:
@ -1024,6 +1038,198 @@ public:
bool isPredictOptimizable() const override { return false; }
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
// Forward declarations for types used in constructors below
class AstCoverTransSet;
class AstCoverSelectExpr;
enum class VCoverBinsType : uint8_t {
USER,
ARRAY,
AUTO,
BINS_IGNORE, // Renamed to avoid Windows macro conflict
BINS_ILLEGAL, // Renamed to avoid Windows macro conflict
DEFAULT,
BINS_WILDCARD, // Renamed to avoid Windows macro conflict
TRANSITION
};
enum class VCoverOptionType : uint8_t {
WEIGHT,
GOAL,
AT_LEAST,
AUTO_BIN_MAX,
PER_INSTANCE,
COMMENT
};
enum class VTransRepType : uint8_t {
NONE, // No repetition
CONSEC, // Consecutive repetition [*]
GOTO, // Goto repetition [->]
NONCONS // Nonconsecutive repetition [=]
};
class AstCoverBin final : public AstNode {
// @astgen op1 := rangesp : List[AstNode]
// @astgen op2 := iffp : Optional[AstNodeExpr]
// @astgen op3 := arraySizep : Optional[AstNodeExpr]
// @astgen op4 := transp : List[AstCoverTransSet]
string m_name;
VCoverBinsType m_type;
bool m_isArray = false;
public:
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
bool isWildcard = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isWildcard ? VCoverBinsType::BINS_WILDCARD
: (isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE
: VCoverBinsType::USER))} {
if (rangesp) addRangesp(rangesp);
}
// Constructor for automatic bins
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{VCoverBinsType::AUTO}
, m_isArray{true} {
this->arraySizep(arraySizep);
}
// Constructor for default bins (catch-all)
AstCoverBin(FileLine* fl, const string& name, VCoverBinsType type)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{type} {}
// Constructor for transition bins
AstCoverBin(FileLine* fl, const string& name, AstCoverTransSet* transp, bool isIgnore,
bool isIllegal, bool isArrayBin = false)
: ASTGEN_SUPER_CoverBin(fl)
, m_name{name}
, m_type{isIllegal ? VCoverBinsType::BINS_ILLEGAL
: (isIgnore ? VCoverBinsType::BINS_IGNORE : VCoverBinsType::TRANSITION)}
, m_isArray{isArrayBin} {
if (transp) addTransp(transp);
}
ASTGEN_MEMBERS_AstCoverBin;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
VCoverBinsType binsType() const { return m_type; }
bool isArray() const { return m_isArray; }
void isArray(bool flag) { m_isArray = flag; }
};
class AstCoverCrossBins final : public AstNode {
// @astgen op1 := selectp : Optional[AstCoverSelectExpr]
string m_name;
public:
AstCoverCrossBins(FileLine* fl, const string& name, AstCoverSelectExpr* selectp)
: ASTGEN_SUPER_CoverCrossBins(fl)
, m_name{name} {
this->selectp(selectp);
}
ASTGEN_MEMBERS_AstCoverCrossBins;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
};
class AstCoverOption final : public AstNode {
// @astgen op1 := valuep : AstNodeExpr
VCoverOptionType m_type;
public:
AstCoverOption(FileLine* fl, VCoverOptionType type, AstNodeExpr* valuep)
: ASTGEN_SUPER_CoverOption(fl)
, m_type{type} {
this->valuep(valuep);
}
ASTGEN_MEMBERS_AstCoverOption;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VCoverOptionType optionType() const { return m_type; }
};
class AstCoverSelectExpr final : public AstNode {
// @astgen op1 := exprp : AstNodeExpr
public:
AstCoverSelectExpr(FileLine* fl, AstNodeExpr* exprp)
: ASTGEN_SUPER_CoverSelectExpr(fl) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverSelectExpr;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverTransItem final : public AstNode {
// Represents a single transition item: value or value[*N] or value[->N] or value[=N]
// @astgen op1 := valuesp : List[AstNode]
// @astgen op2 := repMinp : Optional[AstNodeExpr]
// @astgen op3 := repMaxp : Optional[AstNodeExpr]
VTransRepType m_repType;
public:
AstCoverTransItem(FileLine* fl, AstNode* valuesp, VTransRepType repType = VTransRepType::NONE)
: ASTGEN_SUPER_CoverTransItem(fl)
, m_repType{repType} {
if (valuesp) addValuesp(valuesp);
}
ASTGEN_MEMBERS_AstCoverTransItem;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
VTransRepType repType() const { return m_repType; }
};
class AstCoverTransSet final : public AstNode {
// Represents a transition set: value1 => value2 => value3
// @astgen op1 := itemsp : List[AstCoverTransItem]
public:
AstCoverTransSet(FileLine* fl, AstCoverTransItem* itemsp)
: ASTGEN_SUPER_CoverTransSet(fl) {
if (itemsp) addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverTransSet;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCovergroup final : public AstNode {
// @astgen op1 := argsp : List[AstVar]
// @astgen op2 := membersp : List[AstNode]
// @astgen op3 := eventp : Optional[AstSenTree]
string m_name;
bool m_isClass = false;
public:
AstCovergroup(FileLine* fl, const string& name, AstNode* membersp, AstSenTree* eventp)
: ASTGEN_SUPER_Covergroup(fl)
, m_name{name} {
if (membersp) addMembersp(membersp);
this->eventp(eventp);
}
ASTGEN_MEMBERS_AstCovergroup;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
void name(const string& name) override { m_name = name; }
bool isClass() const { return m_isClass; }
void isClass(bool flag) { m_isClass = flag; }
bool maybePointedTo() const override { return true; }
};
class AstCoverpointRef final : public AstNode {
// @astgen ptr := m_coverpointp : Optional[AstCoverpoint]
string m_name;
public:
AstCoverpointRef(FileLine* fl, const string& name)
: ASTGEN_SUPER_CoverpointRef(fl)
, m_name{name} {}
ASTGEN_MEMBERS_AstCoverpointRef;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
string name() const override VL_MT_STABLE { return m_name; }
AstCoverpoint* coverpointp() const { return m_coverpointp; }
void coverpointp(AstCoverpoint* nodep) { m_coverpointp = nodep; }
};
class AstDefParam final : public AstNode {
// A defparam assignment
// Parents: MODULE
@ -2539,6 +2745,33 @@ public:
void dump(std::ostream& str = std::cout) const override;
void dumpJson(std::ostream& str = std::cout) const override;
};
class AstCoverCross final : public AstNodeFuncCovItem {
// @astgen op1 := itemsp : List[AstCoverpointRef]
// @astgen op2 := binsp : List[AstCoverCrossBins]
// @astgen op3 := optionsp : List[AstCoverOption]
public:
AstCoverCross(FileLine* fl, const string& name, AstCoverpointRef* itemsp)
: ASTGEN_SUPER_CoverCross(fl, name) {
if (itemsp) addItemsp(itemsp);
}
ASTGEN_MEMBERS_AstCoverCross;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
class AstCoverpoint final : public AstNodeFuncCovItem {
// @astgen op1 := exprp : AstNodeExpr
// @astgen op2 := binsp : List[AstCoverBin]
// @astgen op3 := iffp : Optional[AstNodeExpr]
// @astgen op4 := optionsp : List[AstCoverOption]
public:
AstCoverpoint(FileLine* fl, const string& name, AstNodeExpr* exprp)
: ASTGEN_SUPER_Coverpoint(fl, name) {
this->exprp(exprp);
}
ASTGEN_MEMBERS_AstCoverpoint;
void dump(std::ostream& str) const override;
void dumpJson(std::ostream& str) const override;
};
// === AstNodeGen ===
class AstGenBlock final : public AstNodeGen {
@ -2964,4 +3197,5 @@ public:
bool sameNode(const AstNode* /*samep*/) const override { return true; }
};
#endif // Guard

View File

@ -3259,7 +3259,6 @@ void AstCoverToggleDecl::dumpJson(std::ostream& str) const {
std::to_string(range().left()) + ":" + std::to_string(range().right()));
}
}
// NOTE: AstCoverBin and AstCoverpoint dump methods removed - moved to V3AstNodeFuncCov.cpp
void AstCoverInc::dump(std::ostream& str) const {
this->AstNodeStmt::dump(str);
str << " -> ";
@ -3496,3 +3495,139 @@ const char* AstNot::widthMismatch() const VL_MT_STABLE {
BROKEN_RTN(lhsp()->widthMin() != widthMin());
return nullptr;
}
//######################################################################
// Functional coverage dump methods
void AstCovergroup::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
if (m_isClass) str << " [class]";
}
void AstCovergroup::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(name());
if (m_isClass) str << ", \"isClass\": true";
}
void AstCoverpoint::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverpoint::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverBin::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name << " ";
switch (m_type) {
case VCoverBinsType::USER: str << "user"; break;
case VCoverBinsType::ARRAY: str << "array"; break;
case VCoverBinsType::AUTO: str << "auto"; break;
case VCoverBinsType::BINS_IGNORE: str << "ignore"; break;
case VCoverBinsType::BINS_ILLEGAL: str << "illegal"; break;
case VCoverBinsType::DEFAULT: str << "default"; break;
case VCoverBinsType::BINS_WILDCARD: str << "wildcard"; break;
case VCoverBinsType::TRANSITION: str << "transition"; break;
}
if (m_isArray) str << "[]";
}
void AstCoverBin::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
str << ", \"binsType\": ";
switch (m_type) {
case VCoverBinsType::USER: str << "\"user\""; break;
case VCoverBinsType::ARRAY: str << "\"array\""; break;
case VCoverBinsType::AUTO: str << "\"auto\""; break;
case VCoverBinsType::BINS_IGNORE: str << "\"ignore\""; break;
case VCoverBinsType::BINS_ILLEGAL: str << "\"illegal\""; break;
case VCoverBinsType::DEFAULT: str << "\"default\""; break;
case VCoverBinsType::BINS_WILDCARD: str << "\"wildcard\""; break;
case VCoverBinsType::TRANSITION: str << "\"transition\""; break;
}
if (m_isArray) str << ", \"isArray\": true";
}
void AstCoverTransItem::dump(std::ostream& str) const {
this->AstNode::dump(str);
switch (m_repType) {
case VTransRepType::NONE: break;
case VTransRepType::CONSEC: str << " [*]"; break;
case VTransRepType::GOTO: str << " [->]"; break;
case VTransRepType::NONCONS: str << " [=]"; break;
}
}
void AstCoverTransItem::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
if (m_repType != VTransRepType::NONE) {
str << ", \"repType\": ";
switch (m_repType) {
case VTransRepType::NONE: break;
case VTransRepType::CONSEC: str << "\"consec\""; break;
case VTransRepType::GOTO: str << "\"goto\""; break;
case VTransRepType::NONCONS: str << "\"noncons\""; break;
}
}
}
void AstCoverTransSet::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " trans_set";
}
void AstCoverTransSet::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }
void AstCoverCross::dump(std::ostream& str) const { this->AstNodeFuncCovItem::dump(str); }
void AstCoverCross::dumpJson(std::ostream& str) const { this->AstNodeFuncCovItem::dumpJson(str); }
void AstCoverCrossBins::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
}
void AstCoverCrossBins::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
void AstCoverOption::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " ";
switch (m_type) {
case VCoverOptionType::WEIGHT: str << "weight"; break;
case VCoverOptionType::GOAL: str << "goal"; break;
case VCoverOptionType::AT_LEAST: str << "at_least"; break;
case VCoverOptionType::AUTO_BIN_MAX: str << "auto_bin_max"; break;
case VCoverOptionType::PER_INSTANCE: str << "per_instance"; break;
case VCoverOptionType::COMMENT: str << "comment"; break;
}
}
void AstCoverOption::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"optionType\": ";
switch (m_type) {
case VCoverOptionType::WEIGHT: str << "\"weight\""; break;
case VCoverOptionType::GOAL: str << "\"goal\""; break;
case VCoverOptionType::AT_LEAST: str << "\"at_least\""; break;
case VCoverOptionType::AUTO_BIN_MAX: str << "\"auto_bin_max\""; break;
case VCoverOptionType::PER_INSTANCE: str << "\"per_instance\""; break;
case VCoverOptionType::COMMENT: str << "\"comment\""; break;
}
}
void AstCoverpointRef::dump(std::ostream& str) const {
this->AstNode::dump(str);
str << " " << m_name;
}
void AstCoverpointRef::dumpJson(std::ostream& str) const {
this->AstNode::dumpJson(str);
str << ", \"name\": " << VString::quotePercent(m_name);
}
void AstCoverSelectExpr::dump(std::ostream& str) const { this->AstNode::dump(str); }
void AstCoverSelectExpr::dumpJson(std::ostream& str) const { this->AstNode::dumpJson(str); }

View File

@ -797,6 +797,7 @@ class CoverageVisitor final : public VNVisitor {
pair.first->second = varp;
if (m_ftaskp) {
varp->funcLocal(true);
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
m_ftaskp->stmtsp()->addHereThisAsNext(varp);
} else {
m_modp->stmtsp()->addHereThisAsNext(varp);

View File

@ -24,7 +24,7 @@
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
#include "V3CoverageFunctional.h"
#include "V3Covergroup.h"
VL_DEFINE_DEBUG_FUNCTIONS;
@ -1886,7 +1886,7 @@ public:
//######################################################################
// Functional coverage class functions
void V3CoverageFunctional::coverageFunctional(AstNetlist* nodep) {
void V3Covergroup::covergroup(AstNetlist* nodep) {
UINFO(4, __FUNCTION__ << ": " << endl);
{ FunctionalCoverageVisitor{nodep}; } // Destruct before checking
V3Global::dumpCheckGlobalTree("coveragefunc", 0, dumpTreeEitherLevel() >= 3);

View File

@ -1,6 +1,6 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Functional coverage implementation
// DESCRIPTION: Verilator: Covergroup implementation
//
// Code available from: https://verilator.org
//
@ -14,17 +14,17 @@
//
//*************************************************************************
#ifndef VERILATOR_V3COVERAGEFUNCTIONAL_H_
#define VERILATOR_V3COVERAGEFUNCTIONAL_H_
#ifndef VERILATOR_V3COVERGROUP_H_
#define VERILATOR_V3COVERGROUP_H_
#include "V3Ast.h"
#include "V3Error.h"
//============================================================================
class V3CoverageFunctional final {
class V3Covergroup final {
public:
static void coverageFunctional(AstNetlist* nodep);
static void covergroup(AstNetlist* nodep);
};
#endif // Guard

View File

@ -227,12 +227,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
iterateAndNextConstNull(nodep->rhsp());
if (!m_suppressSemi) puts(";\n");
}
void visit(AstAssignDly* nodep) override {
iterateAndNextConstNull(nodep->lhsp());
putfs(nodep, " <= ");
iterateAndNextConstNull(nodep->rhsp());
puts(";\n");
}
void visit(AstAlias* nodep) override {
putbs("alias ");
iterateConst(nodep->itemsp());

View File

@ -309,6 +309,7 @@ class LinkIncVisitor final : public VNVisitor {
AstVar* const varp = new AstVar{
fl, VVarType::BLOCKTEMP, name, VFlagChildDType{},
new AstRefDType{fl, AstRefDType::FlagTypeOfExpr{}, readp->cloneTree(true)}};
varp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
if (m_ftaskp) varp->funcLocal(true);
// Declare the variable

View File

@ -37,7 +37,7 @@
#include "V3Const.h"
#include "V3Control.h"
#include "V3Coverage.h"
#include "V3CoverageFunctional.h"
#include "V3Covergroup.h"
#include "V3CoverageJoin.h"
#include "V3Dead.h"
#include "V3Delayed.h"
@ -239,7 +239,7 @@ static void process() {
// Functional coverage code generation
// Generate code for covergroups/coverpoints
V3CoverageFunctional::coverageFunctional(v3Global.rootp());
V3Covergroup::covergroup(v3Global.rootp());
// Resolve randsequence if they are used by the design
if (v3Global.useRandSequence()) V3RandSequence::randSequenceNetlist(v3Global.rootp());

View File

@ -1,18 +0,0 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# 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-FileCopyrightText: 2026 Wilson Snyder
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
import vltest_bootstrap
test.scenarios('vlt')
test.compile(verilator_flags2=["--exe", "t/t_funccov_basic_main.cpp"], make_main=False)
test.execute()
test.passes()

View File

@ -1,25 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// Test basic functional coverage infrastructure
module t;
/* verilator lint_off UNSIGNED */
int addr;
int cmd;
// For now, this is just a placeholder until parser support is added
// We'll test the runtime infrastructure directly from C++
initial begin
addr = 10;
cmd = 1;
$display("Test placeholder for functional coverage");
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,112 +0,0 @@
// DESCRIPTION: Verilator: Verilog Test main for functional coverage
//
// 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-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
#include "verilated.h"
#include "verilated_funccov.h"
#include VM_PREFIX_INCLUDE
#include <iostream>
#include <memory>
// Test the functional coverage runtime infrastructure
void testFuncCovInfrastructure() {
std::cout << "Testing functional coverage infrastructure..." << std::endl;
// Create a covergroup
VerilatedCovergroup cg("test_cg");
// Create a coverpoint with automatic bins
auto* cp_addr = new VerilatedCoverpoint{"cp_addr"};
cp_addr->addBin(new VerilatedCoverRangeBin{"low", 0, 127});
cp_addr->addBin(new VerilatedCoverRangeBin{"high", 128, 255});
cg.addCoverpoint(cp_addr);
// Create another coverpoint
auto* cp_cmd = new VerilatedCoverpoint{"cp_cmd"};
cp_cmd->addBin(new VerilatedCoverRangeBin{"read", 0, 0});
cp_cmd->addBin(new VerilatedCoverRangeBin{"write", 1, 1});
cg.addCoverpoint(cp_cmd);
// Create a cross coverage
auto* cross = new VerilatedCoverCross{"cross_cmd_addr"};
cross->addCoverpoint(cp_addr);
cross->addCoverpoint(cp_cmd);
cg.addCross(cross);
// Sample some values
std::cout << "Sampling values..." << std::endl;
cp_addr->sample(10); // low bin
cp_cmd->sample(0); // read bin
cross->sample({10, 0});
cp_addr->sample(200); // high bin
cp_cmd->sample(1); // write bin
cross->sample({200, 1});
cp_addr->sample(50); // low bin again
cp_cmd->sample(0); // read bin again
cross->sample({50, 0});
// Check coverage
double addr_cov = cp_addr->getCoverage();
double cmd_cov = cp_cmd->getCoverage();
double cross_cov = cross->getCoverage();
double cg_cov = cg.get_coverage();
std::cout << "cp_addr coverage: " << addr_cov << "%" << std::endl;
std::cout << "cp_cmd coverage: " << cmd_cov << "%" << std::endl;
std::cout << "cross coverage: " << cross_cov << "%" << std::endl;
std::cout << "Covergroup coverage: " << cg_cov << "%" << std::endl;
// Verify results
if (addr_cov != 100.0) {
std::cerr << "ERROR: Expected addr coverage 100%, got " << addr_cov << std::endl;
exit(1);
}
if (cmd_cov != 100.0) {
std::cerr << "ERROR: Expected cmd coverage 100%, got " << cmd_cov << std::endl;
exit(1);
}
// Cross coverage should be 50% (2 out of 4 possible cross products covered)
if (cross_cov < 49.9 || cross_cov > 50.1) {
std::cerr << "ERROR: Expected cross coverage ~50%, got " << cross_cov << std::endl;
exit(1);
}
// Overall covergroup coverage is weighted average of all components
// (100 + 100 + 50) / 3 = 83.33%
if (cg_cov < 83.0 || cg_cov > 84.0) {
std::cerr << "ERROR: Expected covergroup coverage ~83.33%, got " << cg_cov << std::endl;
exit(1);
}
std::cout << "Functional coverage infrastructure test PASSED" << std::endl;
}
int main(int argc, char** argv) {
// Standard Verilator setup
const std::unique_ptr<VerilatedContext> contextp{new VerilatedContext};
contextp->commandArgs(argc, argv);
const std::unique_ptr<VM_PREFIX> topp{new VM_PREFIX{contextp.get()}};
// Test functional coverage infrastructure
testFuncCovInfrastructure();
// Run the Verilog simulation briefly
contextp->timeInc(1);
topp->eval();
// Check for finish
if (!contextp->gotFinish()) {
VL_PRINTF("%%Error: main.cpp didn't finish\n");
exit(1);
}
std::cout << "*-* All Finished *-*" << std::endl;
return 0;
}

View File

@ -44,8 +44,7 @@ for dfile in test.glob_some(test.obj_dir + "/*.d"):
hit[filename] = True
for filename in sorted(hit.keys()):
if (not hit[filename] and filename != "verilated_funccov.h"
and not re.search(r'_sc', filename) and not re.search(r'_fst', filename)
if (not hit[filename] and not re.search(r'_sc', filename) and not re.search(r'_fst', filename)
and not re.search(r'_saif', filename) and not re.search(r'_thread', filename)
and (not re.search(r'_timing', filename) or test.have_coroutines)):
test.error("Include file not covered by t_verilated_all test: ", filename)