Merge branch 'master' into fix/73-dist

This commit is contained in:
Yilou Wang 2026-03-02 23:11:08 +01:00
commit 31aeced75b
37 changed files with 1012 additions and 699 deletions

View File

@ -155,7 +155,7 @@ jobs:
- name: Zip up repository
run: Compress-Archive -LiteralPath install -DestinationPath verilator.zip
- name: Upload zip archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/repo/verilator.zip
name: verilator-win.zip

View File

@ -77,7 +77,7 @@ jobs:
uses: actions/checkout@v6
- name: Download code coverage data
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: code-coverage-*
path: obj_coverage
@ -114,7 +114,7 @@ jobs:
sudo apt install lcov
- name: Download repository archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: ${{ needs.build.outputs.archive }}
path: ${{ github.workspace }}
@ -125,7 +125,7 @@ jobs:
ls -lsha
- name: Download code coverage data
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: code-coverage-*
path: repo/obj_coverage
@ -170,14 +170,14 @@ jobs:
fi
- name: Upload report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: repo/obj_coverage
name: coverage-report
- name: Upload notification
if: ${{ github.event_name == 'pull_request' }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: repo/notification
name: coverage-pr-notification

View File

@ -93,7 +93,7 @@ jobs:
echo "archive=$ARCHIVE" >> "$GITHUB_OUTPUT"
- name: Upload repository archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/${{ steps.create-archive.outputs.archive }}
name: ${{ steps.create-archive.outputs.archive }}

View File

@ -70,7 +70,7 @@ jobs:
run: tar --posix -c -z -f verilator-rtlmeter.tar.gz install
- name: Upload Verilator installation archive
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: verilator-rtlmeter.tar.gz
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}

View File

@ -64,7 +64,7 @@ jobs:
sudo apt install ccache mold libfl-dev libgoogle-perftools-dev libsystemc-dev
- name: Download Verilator installation archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: verilator-rtlmeter-${{ inputs.runs-on }}-${{ inputs.cc }}
@ -117,7 +117,7 @@ jobs:
./rtlmeter report --steps '*' --metrics '*' ../results-${{ steps.results.outputs.hash }}.json
- name: Upload results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: results-${{ steps.results.outputs.hash }}.json
name: rtlmeter-${{ inputs.tag }}-results-${{ steps.results.outputs.hash }}

View File

@ -55,7 +55,7 @@ jobs:
steps:
- name: Download repository archive
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: ${{ inputs.archive }}
path: ${{ github.workspace }}
@ -99,7 +99,7 @@ jobs:
- name: Upload code coverage data
if: ${{ inputs.dev-gcov }}
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: ${{ github.workspace }}/repo/obj_coverage/verilator-${{ inputs.suite }}.info
name: code-coverage-${{ inputs.suite }}

View File

@ -20,13 +20,13 @@ jobs:
pull-requests: write
steps:
- name: Download report
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: rtlmeter-pr-results
run-id: ${{ github.event.workflow_run.id }}
github-token: ${{ secrets.GITHUB_TOKEN }}
- name: Download PR number
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: pr-number
run-id: ${{ github.event.workflow_run.id }}

View File

@ -198,7 +198,7 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Download all results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: rtlmeter-${{ matrix.tag }}-results-*
path: all-results-${{ matrix.tag }}
@ -208,7 +208,7 @@ jobs:
run: |
./rtlmeter collate ../all-results-${{ matrix.tag }}/*.json > ../all-results-${{ matrix.tag }}.json
- name: Upload combined results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: all-results-${{ matrix.tag }}.json
name: all-results-${{ matrix.tag }}
@ -227,13 +227,13 @@ jobs:
runs-on: ubuntu-24.04
steps:
- name: Download combined results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: all-results-*
path: results
merge-multiple: true
- name: Upload published results
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: results/*.json
name: published-results
@ -289,7 +289,7 @@ jobs:
working-directory: rtlmeter
run: make venv
- name: Download combined results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
pattern: all-results-*
path: all-results
@ -308,7 +308,7 @@ jobs:
DATE=$(gh run --repo ${{ github.repository }} view $ID --json createdAt --jq ".createdAt")
echo "date=$DATE" >> $GITHUB_OUTPUT
- name: Download scheduled run results
uses: actions/download-artifact@v7
uses: actions/download-artifact@v8
with:
name: published-results
path: nightly-results
@ -374,14 +374,14 @@ jobs:
done
cat report.txt
- name: Upload report
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: report.txt
name: rtlmeter-pr-results
- name: Save PR number
run: echo ${{ github.event.number }} > pr-number.txt
- name: Upload PR number
uses: actions/upload-artifact@v6
uses: actions/upload-artifact@v7
with:
path: pr-number.txt
name: pr-number

View File

@ -99,6 +99,7 @@ Ivan Vnučec
Iztok Jeras
Jake Merdich
Jakub Wasilewski
jalcim
James Bailey
James Hanlon
James Hutchinson

View File

@ -496,6 +496,9 @@ bool VlRandomizer::next(VlRNG& rngr) {
std::iostream& os = getSolver();
if (!os) return false;
// Soft constraint relaxation (IEEE 1800-2017 18.5.13, last-wins priority):
// Try hard + soft[0..N-1], then hard + soft[1..N-1], ..., then hard only.
// First SAT phase wins. If hard-only is UNSAT, report via unsat-core.
os << "(set-option :produce-models true)\n";
os << "(set-logic QF_ABV)\n";
os << "(define-fun __Vbv ((b Bool)) (_ BitVec 1) (ite b #b1 #b0))\n";
@ -520,9 +523,20 @@ bool VlRandomizer::next(VlRNG& rngr) {
os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n";
}
os << "(check-sat)\n";
const size_t nSoft = m_softConstraints.size();
bool sat = false;
for (size_t phase = 0; phase <= nSoft && !sat; ++phase) {
const bool hasSoft = (phase < nSoft);
if (hasSoft) {
os << "(push 1)\n";
for (size_t i = phase; i < nSoft; ++i)
os << "(assert (= #b1 " << m_softConstraints[i] << "))\n";
}
os << "(check-sat)\n";
sat = parseSolution(os, /*log=*/phase == nSoft);
if (!sat && hasSoft) os << "(pop 1)\n";
}
bool sat = parseSolution(os, true);
if (!sat) {
// If unsat, use named assertions to get unsat-core
os << "(reset)\n";
@ -541,8 +555,11 @@ bool VlRandomizer::next(VlRNG& rngr) {
}
int j = 0;
for (const std::string& constraint : m_constraints) {
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j << "))\n";
j++;
os << "(assert (! (= #b1 " << constraint << ") :named cons" << j++ << "))\n";
}
for (const auto& pair : randcPinned) {
const int w = m_vars.at(pair.first)->width();
os << "(assert (= " << pair.first << " (_ bv" << pair.second << " " << w << ")))\n";
}
os << "(check-sat)\n";
sat = parseSolution(os, true);
@ -550,6 +567,7 @@ bool VlRandomizer::next(VlRNG& rngr) {
os << "(reset)\n";
return false;
}
for (int i = 0; i < _VL_SOLVER_HASH_LEN_TOTAL && sat; ++i) {
os << "(assert ";
randomConstraint(os, rngr, _VL_SOLVER_HASH_LEN);
@ -723,15 +741,22 @@ void VlRandomizer::hard(std::string&& constraint, const char* filename, uint32_t
}
}
void VlRandomizer::soft(std::string&& constraint, const char* /*filename*/, uint32_t /*linenum*/,
const char* /*source*/) {
m_softConstraints.emplace_back(std::move(constraint));
}
void VlRandomizer::clearConstraints() {
m_constraints.clear();
m_constraints_line.clear();
m_solveBefore.clear();
m_softConstraints.clear();
// Keep m_vars for class member randomization
}
void VlRandomizer::clearAll() {
m_constraints.clear();
m_softConstraints.clear();
m_vars.clear();
m_randcVarNames.clear();
m_randcValueQueues.clear();

View File

@ -200,9 +200,10 @@ public:
// Object holding constraints and variable references.
class VlRandomizer VL_NOT_FINAL {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
std::vector<std::string> m_constraints; // Solver-dependent hard constraints
std::vector<std::string>
m_constraints_line; // fileline content of the constraint for unsat constraints
std::vector<std::string> m_softConstraints; // Soft constraints
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
// variables
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
@ -593,6 +594,8 @@ public:
void hard(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
const char* source = "");
void soft(std::string&& constraint, const char* filename = "", uint32_t linenum = 0,
const char* source = "");
void clearConstraints();
void clearAll(); // Clear both constraints and variables
void markRandc(const char* name); // Mark variable as randc for cyclic tracking

View File

@ -65,8 +65,9 @@ set(HEADERS
V3Clock.h
V3Combine.h
V3Common.h
V3Control.h
V3Const.h
V3Container.h
V3Control.h
V3Coverage.h
V3CoverageJoin.h
V3Dead.h

View File

@ -815,6 +815,7 @@ public:
RANDOMIZER_CLEARCONSTRAINTS,
RANDOMIZER_CLEARALL,
RANDOMIZER_HARD,
RANDOMIZER_SOFT,
RANDOMIZER_UNIQUE,
RANDOMIZER_MARK_RANDC,
RANDOMIZER_SOLVE_BEFORE,
@ -951,6 +952,7 @@ inline std::ostream& operator<<(std::ostream& os, const VCMethod& rhs) {
{RANDOMIZER_CLEARCONSTRAINTS, "clearConstraints", false}, \
{RANDOMIZER_CLEARALL, "clearAll", false}, \
{RANDOMIZER_HARD, "hard", false}, \
{RANDOMIZER_SOFT, "soft", false}, \
{RANDOMIZER_UNIQUE, "rand_unique", false}, \
{RANDOMIZER_MARK_RANDC, "markRandc", false}, \
{RANDOMIZER_SOLVE_BEFORE, "solveBefore", false}, \

65
src/V3Container.h Normal file
View File

@ -0,0 +1,65 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Generic container types
//
// 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: 2003-2026 Wilson Snyder
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3CONTAINER_H_
#define VERILATOR_V3CONTAINER_H_
#include "config_build.h"
#include "verilatedos.h"
#include <unordered_map>
#include <unordered_set>
#include <vector>
//============================================================================
// Similar to std::set, but ordered based on call order to emplace. Used
// when insertion order is desired (e.g. std::vector), but duplicates need removal.
// Keys may not be modified. (If needed in future, m_set could contain vector positions.)
template <typename T_Key>
class VInsertionSet final {
std::vector<T_Key> m_keys; // Elements by insertion order
std::unordered_set<T_Key> m_keySet; // Elements by key
public:
// METHODS
bool insert(const T_Key& key) {
// Returns if did insertion (second pair argument of traditional emplace)
const auto itFoundPair = m_keySet.insert(key);
if (itFoundPair.second) m_keys.push_back(key);
return itFoundPair.second;
}
void clear() {
m_keys.clear();
m_keySet.clear();
}
// ACCESSORS
bool empty() const { return m_keys.empty(); }
bool exists(const T_Key& key) const { return m_keySet.find(key) != m_keySet.end(); }
// ITERATORS
using const_iterator = typename std::vector<T_Key>::const_iterator;
const_iterator begin() const { return m_keys.begin(); }
const_iterator end() const { return m_keys.end(); }
};
//============================================================================
// VInsertionMap: Not currently needed; prototype code exists, just ask.
// Similar to std::map, but ordered based on call order to emplace. Used
// when insertion order is desired (e.g. std::vector), but duplicates need removal.
// Values may be modified.
#endif // Guard

View File

@ -263,7 +263,7 @@ class DelayedVisitor final : public VNVisitor {
AstUser3Allocator<AstVarScope, std::vector<WriteReference>> m_writeRefs;
// STATE - across all visitors
std::set<AstSenTree*> m_timingDomains; // Timing resume domains
VInsertionSet<AstSenTree*> m_timingDomains; // Timing resume domains
// STATE - for current visit position (use VL_RESTORER)
AstActive* m_activep = nullptr; // Current activate

View File

@ -734,8 +734,8 @@ public:
puts(";\n");
}
void visit(AstCNew* nodep) override {
if (VN_IS(nodep->dtypep(), VoidDType)) {
// super.new case
if (VN_IS(nodep->dtypep(), VoidDType)) { // super.new case
putsDecoration(nodep, "/*super.new*/");
return;
}
// assignment case;

View File

@ -18,6 +18,7 @@
#include "V3Options.h"
#include "V3Container.h"
#include "V3Error.h"
#include "V3File.h"
#include "V3Global.h"
@ -70,30 +71,26 @@ public:
std::list<string> m_lineArgs; // List of command line argument encountered
// List of arguments encounterd, and a bool in needed for rerunning --dump-inputs
std::list<std::pair<std::list<std::string>, bool>> m_allArgs;
std::list<string> m_incDirUsers; // Include directories (ordered)
std::set<string> m_incDirUserSet; // Include directories (for removing duplicates)
VInsertionSet<std::string> m_incDirUsers; // Include directories (ordered)
std::list<string> m_incDirFallbacks; // Include directories (ordered)
std::set<string> m_incDirFallbackSet; // Include directories (for removing duplicates)
std::map<const string, V3LangCode> m_langExts; // Language extension map
std::list<string> m_libExtVs; // Library extensions (ordered)
std::set<string> m_libExtVSet; // Library extensions (for removing duplicates)
VInsertionSet<std::string> m_libExtVs; // Library extensions (ordered)
DirMap m_dirMap; // Directory listing
// ACCESSOR METHODS
void addIncDirUser(const string& incdir) {
const string& dir = V3Os::filenameCleanup(incdir);
const auto itFoundPair = m_incDirUserSet.insert(dir);
if (itFoundPair.second) {
const bool inserted = m_incDirUsers.insert(dir);
if (inserted) {
// cppcheck-suppress stlFindInsert // cppcheck 1.90 bug
m_incDirUsers.push_back(dir);
m_incDirFallbacks.remove(dir); // User has priority over Fallback
m_incDirFallbackSet.erase(dir); // User has priority over Fallback
}
}
void addIncDirFallback(const string& incdir) {
const string& dir = V3Os::filenameCleanup(incdir);
if (m_incDirUserSet.find(dir)
== m_incDirUserSet.end()) { // User has priority over Fallback
if (!m_incDirUsers.exists(dir)) { // User has priority over Fallback
const auto itFoundPair = m_incDirFallbackSet.insert(dir);
if (itFoundPair.second) m_incDirFallbacks.push_back(dir);
}
@ -106,10 +103,7 @@ public:
m_langExts[addext] = lc;
}
void addLibExtV(const string& libext) {
const auto itFoundPair = m_libExtVSet.insert(libext);
if (itFoundPair.second) m_libExtVs.push_back(libext);
}
void addLibExtV(const string& libext) { m_libExtVs.insert(libext); }
V3OptionsImp() = default;
~V3OptionsImp() = default;
};

View File

@ -26,6 +26,7 @@
#include "V3Ast.h"
#include "V3Broken.h"
#include "V3Container.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3FunctionTraits.h"

View File

@ -28,6 +28,7 @@
#include "V3Ast.h"
#include "V3Broken.h"
#include "V3Container.h"
#include "V3Error.h"
#include "V3FileLine.h"
#include "V3FunctionTraits.h"

View File

@ -91,6 +91,8 @@ class PremitVisitor final : public VNVisitor {
// Keep as local temporary.
const std::string name = "__Vtemp_" + std::to_string(++m_tmpVarCnt);
varp = new AstVar{flp, VVarType::STMTTEMP, name, nodep->dtypep()};
varp->funcLocal(true);
varp->noReset(true);
m_cfuncp->addVarsp(varp);
++m_temporaryVarsCreated;

View File

@ -1906,12 +1906,14 @@ class ConstraintExprVisitor final : public VNVisitor {
VL_DO_DANGLING(nodep->deleteTree(), nodep);
return;
}
// Only hard constraints are currently supported
// Emit as soft or hard constraint per IEEE 1800-2017 18.5.13
const VCMethod method
= nodep->isSoft() ? VCMethod::RANDOMIZER_SOFT : VCMethod::RANDOMIZER_HARD;
AstCMethodHard* const callp = new AstCMethodHard{
nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(m_genp->user2p(), NodeModule), m_genp,
VAccess::READWRITE},
VCMethod::RANDOMIZER_HARD, nodep->exprp()->unlinkFrBack()};
method, nodep->exprp()->unlinkFrBack()};
callp->dtypeSetVoid();
// Pass filename, lineno, and source as separate arguments
// This allows EmitC to call protect() on filename, similar to VL_STOP
@ -2178,6 +2180,7 @@ class CaptureVisitor final : public VNVisitor {
std::map<const AstVar*, AstVar*> m_varCloneMap; // Map original var nodes to their clones
std::set<AstNode*> m_ignore; // Nodes to ignore for capturing
AstVar* m_thisp = nullptr; // Variable for outer context's object, if necessary
bool m_staticContext = false; // True when capturing from a static function context
// METHODS
@ -2250,6 +2253,11 @@ class CaptureVisitor final : public VNVisitor {
if (varIsParam) return CaptureMode::CAP_VALUE;
// Static var in function (will not be inlined, because it's in class)
if (callerIsClass && varIsFuncLocal) return CaptureMode::CAP_VALUE;
// Static class members in static context don't need 'this' capture;
// V3Class will move both the function and the member to __Vclpkg
if (m_staticContext && callerIsClass && varIsFieldOfCaller
&& varRefp->varp()->lifetime().isStatic())
return CaptureMode::CAP_NO;
if (callerIsClass && varIsFieldOfCaller) return CaptureMode::CAP_THIS;
UASSERT_OBJ(!callerIsClass, varRefp, "Invalid reference?");
return CaptureMode::CAP_VALUE;
@ -2370,10 +2378,12 @@ class CaptureVisitor final : public VNVisitor {
void visit(AstNode* nodep) override { iterateChildren(nodep); }
public:
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const targetp)
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const targetp,
bool staticContext = false)
: m_argsp{nullptr}
, m_callerp{callerp}
, m_targetp{targetp} {
, m_targetp{targetp}
, m_staticContext{staticContext} {
iterateAndNextNull(nodep);
}
@ -3945,9 +3955,16 @@ class RandomizeVisitor final : public VNVisitor {
if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
// Handle std::randomize; create wrapper function that calls basicStdRandomization on
// each varref argument, then transform nodep to call that wrapper
const bool inStaticContext = m_ftaskp && m_ftaskp->isStatic();
AstVar* const stdrand = createStdRandomGenerator(m_modp);
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc(
m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep));
// When called from a static function, mark helper and stdrand as static
// so V3Class moves them to __Vclpkg alongside the calling function
if (inStaticContext) {
stdrand->lifetime(VLifetime::STATIC_EXPLICIT);
randomizeFuncp->isStatic(true);
}
randomizeFuncp->addStmtsp(
new AstAssign{nodep->fileline(),
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
@ -4003,7 +4020,8 @@ class RandomizeVisitor final : public VNVisitor {
}
if (withp) {
FileLine* const fl = nodep->fileline();
withCapturep = std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr);
withCapturep = std::make_unique<CaptureVisitor>(withp->exprp(), m_modp, nullptr,
inStaticContext);
withCapturep->addFunctionArguments(randomizeFuncp);
// Clear old constraints and variables for std::randomize with clause
if (stdrand) {

View File

@ -259,6 +259,7 @@ class ScopeVisitor final : public VNVisitor {
clonep = nodep->cloneTree(false);
}
nodep->user2p(clonep);
clonep->user2p(clonep); // For recursive self-references after cloneTree
m_scopep->addBlocksp(clonep);
// We iterate under the *clone*
iterateChildren(clonep);

View File

@ -226,15 +226,37 @@ static void warnNoSplit(const AstVar* varp, const AstNode* wherep, const char* r
// Split Unpacked Variables
// Replacement policy:
// AstArraySel -> Just replace with the AstVarRef for the split variable
// AstVarRef -> Create a temporary variable and refer the variable
// AstSliceSel -> Create a temporary variable and refer the variable
// AstVarRef -> Create a temporary variable and refer to the variable
// AstSliceSel -> Create a temporary variable and refer to the variable
// Compare AstNode* to get deterministic ordering when showing messages.
// Track order-of-encounter for nodes, so we are stable, versus comparing node pointers
// (fileline may be the same across multiple nodes, so is insufficient)
class SplitNodeOrder final {
// NODE STATE
// AstNode::user4() -> uint64_t. Order the node is in the tree
const VNUser4InUse m_user4InUse;
public:
static uint64_t nextId() {
static uint64_t s_sequence = 0;
return ++s_sequence;
}
static uint64_t nodeOrder(const AstNode* const nodep) {
AstNode* const ncnodep = const_cast<AstNode*>(nodep);
const uint64_t id = ncnodep->user4();
if (VL_LIKELY(id)) return id;
ncnodep->user4(nextId());
return ncnodep->user4();
}
};
// Compare AstNode* to get deterministic ordering
struct AstNodeComparator final {
bool operator()(const AstNode* ap, const AstNode* bp) const {
// First consider lines, as makes messages to user more obvious
const int lineComp = ap->fileline()->operatorCompare(*bp->fileline());
if (lineComp != 0) return lineComp < 0;
return ap < bp;
return SplitNodeOrder::nodeOrder(ap) < SplitNodeOrder::nodeOrder(bp);
}
};
@ -247,6 +269,7 @@ class UnpackRef final {
const int m_lsb; // for SliceSel
const VAccess m_access;
const bool m_ftask; // true if the reference is in function/task. false if in module.
public:
UnpackRef(AstNode* stmtp, AstVarRef* nodep, bool ftask)
: m_contextp{stmtp}
@ -587,12 +610,12 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl {
iterateChildren(nodep);
}
}
AstVarRef* createTempVar(AstNode* context, AstNode* nodep, AstUnpackArrayDType* dtypep,
AstVarRef* createTempVar(AstNode* contextp, AstNode* nodep, AstUnpackArrayDType* dtypep,
const std::string& name_prefix, std::vector<AstVar*>& vars,
int start_idx, bool lvalue, bool /*ftask*/) {
FileLine* const fl = nodep->fileline();
const std::string name = m_tempNames.get(nodep) + "__" + name_prefix;
AstNodeAssign* const assignp = VN_CAST(context, NodeAssign);
AstNodeAssign* const assignp = VN_CAST(contextp, NodeAssign);
if (assignp) {
// "always_comb a = b;" to "always_comb begin a = b; end" so that local
// variable can be added.
@ -604,7 +627,7 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl {
<< " is created lsb:" << dtypep->lo() << " msb:" << dtypep->hi());
// Use AstAssign if true, otherwise AstAssignW
const bool use_simple_assign
= (context && VN_IS(context, NodeFTaskRef)) || (assignp && VN_IS(assignp, Assign));
= (contextp && VN_IS(contextp, NodeFTaskRef)) || (assignp && VN_IS(assignp, Assign));
for (int i = 0; i < dtypep->elementsConst(); ++i) {
AstNodeExpr* lhsp
@ -618,11 +641,11 @@ class SplitUnpackedVarVisitor final : public VNVisitor, public SplitVarImpl {
AstAssign* const ap = new AstAssign{fl, lhsp, rhsp};
if (lvalue) {
// If varp is LHS, this assignment must appear after the original
// assignment(context).
context->addNextHere(ap);
// assignment(contextp).
contextp->addNextHere(ap);
} else {
// If varp is RHS, this assignment comes just before the original assignment
context->addHereThisAsNext(ap);
contextp->addHereThisAsNext(ap);
}
UASSERT_OBJ(!m_contextp, m_contextp, "must be null");
setContextAndIterate(ap, refp);
@ -1364,6 +1387,7 @@ const char* SplitVarImpl::cannotSplitPackedVarReason(const AstVar* varp) {
void V3SplitVar::splitVariable(AstNetlist* nodep) {
UINFO(2, __FUNCTION__ << ":");
SplitNodeOrder order;
SplitVarRefs refs;
{
const SplitUnpackedVarVisitor visitor{nodep};

View File

@ -1856,7 +1856,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
if (!makeChanges) return tconnects;
// Connect missing ones
std::set<const AstVar*> argWrap; // Which ports are defaulted, forcing arg wrapper creation
VInsertionSet<const AstVar*> argWrap; // Which ports are defaulted; need arg wrapper creation
for (int i = 0; i < tpinnum; ++i) {
AstVar* const portp = tconnects[i].first;
if (!tconnects[i].second || !tconnects[i].second->exprp()) {
@ -1882,7 +1882,7 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
if (statep) {
portp->pinNum(i + 1); // Make sure correct, will use to build name
UINFO(9, "taskConnects arg wrapper needed " << portp->valuep());
argWrap.emplace(portp);
argWrap.insert(portp);
} else { // statep = nullptr, called too late or otherwise to handle args
// Problem otherwise is we might have a varref, task
// call, or something else that only makes sense in the
@ -1956,7 +1956,8 @@ V3TaskConnects V3Task::taskConnects(AstNodeFTaskRef* nodep, AstNode* taskStmtsp,
}
void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconnects,
V3TaskConnectState* statep, const std::set<const AstVar*>& argWrap) {
V3TaskConnectState* statep,
const VInsertionSet<const AstVar*>& argWrap) {
statep->setDidWrap();
// Make wrapper name such that is same iff same args are defaulted
std::string newname = nodep->name() + "__Vtcwrap";
@ -1973,7 +1974,7 @@ void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconn
for (const auto& tconnect : tconnects) {
const AstVar* const portp = tconnect.first;
AstArg* const argp = tconnect.second;
if (argWrap.find(portp) != argWrap.end()) { // Removed arg
if (argWrap.exists(portp)) { // Removed arg
statep->pushDeletep(argp->unlinkFrBack());
}
}
@ -1985,7 +1986,7 @@ void V3Task::taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconn
AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newname,
const V3TaskConnects& tconnects,
const std::set<const AstVar*>& argWrap) {
const VInsertionSet<const AstVar*>& argWrap) {
std::map<const AstVar*, AstVar*> oldNewVars; // Old -> new var mappings
AstNodeFTask* const newTaskp = taskp->cloneType(newname);
@ -2019,7 +2020,7 @@ AstNodeFTask* V3Task::taskConnectWrapNew(AstNodeFTask* taskp, const string& newn
for (const auto& tconnect : tconnects) {
AstVar* const portp = tconnect.first;
AstVar* newPortp;
if (argWrap.find(portp) == argWrap.end()) { // Not removed arg
if (!argWrap.exists(portp)) { // Not removed arg
newPortp = new AstVar{portp->fileline(), portp->varType(), portp->name(), portp};
newPortp->propagateWrapAttrFrom(portp);
newPortp->funcLocal(true);

View File

@ -21,6 +21,7 @@
#include "verilatedos.h"
#include "V3Ast.h"
#include "V3Container.h"
#include "V3Error.h"
#include <utility>
@ -58,10 +59,10 @@ public:
bool makeChanges = true) VL_MT_DISABLED;
static void taskConnectWrap(AstNodeFTaskRef* nodep, const V3TaskConnects& tconnects,
V3TaskConnectState* statep,
const std::set<const AstVar*>& argWrap) VL_MT_DISABLED;
static AstNodeFTask* taskConnectWrapNew(AstNodeFTask* taskp, const string& newname,
const V3TaskConnects& tconnects,
const std::set<const AstVar*>& argWrap) VL_MT_DISABLED;
const VInsertionSet<const AstVar*>& argWrap) VL_MT_DISABLED;
static AstNodeFTask*
taskConnectWrapNew(AstNodeFTask* taskp, const string& newname, const V3TaskConnects& tconnects,
const VInsertionSet<const AstVar*>& argWrap) VL_MT_DISABLED;
static string assignInternalToDpi(AstVar* portp, bool isPtr, const string& frSuffix,
const string& toSuffix,
const string& frPrefix = "") VL_MT_DISABLED;

View File

@ -285,13 +285,9 @@ static void process() {
}
if (!v3Global.opt.serializeOnly()) {
// Lift expressions out of statements. Currently disabled for line and
// expression coverage, as otherwise later V3Split would further split
// combinational always blocks and alter counts (that needs to be fixed in V3Split)
if (v3Global.opt.fLiftExpr() //
&& !v3Global.opt.coverageLine() && !v3Global.opt.coverageExpr()) {
V3LiftExpr::liftExprAll(v3Global.rootp());
}
// Lift expressions out of statements.
if (v3Global.opt.fLiftExpr()) V3LiftExpr::liftExprAll(v3Global.rootp());
// Move assignments from X into MODULE temps.
// (Before flattening, so each new X variable is shared between all scopes of that
// module.)

View File

@ -449,12 +449,12 @@
logic unsigned [15:0] b;
} pstruct;
000021 function logic func_side_effect;
+000021 point: type=line comment=block hier=top.t.cond1
000021 $display("SIDE EFFECT");
+000021 point: type=line comment=block hier=top.t.cond1
000021 return 1;
+000021 point: type=line comment=block hier=top.t.cond1
%000001 function logic func_side_effect;
-000001 point: type=line comment=block hier=top.t.cond1
%000001 $display("SIDE EFFECT");
-000001 point: type=line comment=block hier=top.t.cond1
%000001 return 1;
-000001 point: type=line comment=block hier=top.t.cond1
endfunction
000010 function arr_t get_arr;
@ -465,14 +465,14 @@
+000010 point: type=line comment=block hier=top.t.cond1
endfunction
~000031 assign a = (cyc == 0) ? clk : 1'bz;
~000011 assign a = (cyc == 0) ? clk : 1'bz;
-000000 point: type=branch comment=cond_then hier=top.t.cond1
+000031 point: type=branch comment=cond_else hier=top.t.cond1
+000011 point: type=branch comment=cond_else hier=top.t.cond1
~000028 assign b = (cyc == 1) ? clk : 0;
-000003 point: type=branch comment=cond_then hier=top.t.cond1
+000028 point: type=branch comment=cond_else hier=top.t.cond1
~000021 assign c = func_side_effect() ? clk : 0;
+000021 point: type=branch comment=cond_then hier=top.t.cond1
%000001 assign c = func_side_effect() ? clk : 0;
-000001 point: type=branch comment=cond_then hier=top.t.cond1
-000000 point: type=branch comment=cond_else hier=top.t.cond1
000010 always @(posedge clk) begin
+000010 point: type=line comment=block hier=top.t.cond1
@ -500,9 +500,9 @@
assign m = tab[clk ? 3 : 4];
for (genvar i = 0; i < 2; i++) begin
000011 assign g = clk ? 1 : 0;
+000010 point: type=branch comment=cond_then hier=top.t.cond1
+000011 point: type=branch comment=cond_else hier=top.t.cond1
000022 assign g = clk ? 1 : 0;
+000020 point: type=branch comment=cond_then hier=top.t.cond1
+000022 point: type=branch comment=cond_else hier=top.t.cond1
end
000011 always begin

View File

@ -150,20 +150,20 @@ DA:304,1
DA:305,20
DA:306,20
DA:315,1
DA:323,21
DA:324,21
DA:325,21
DA:323,1
DA:324,1
DA:325,1
DA:328,10
DA:329,10
DA:330,10
DA:333,31
DA:333,11
BRDA:333,0,0,0
BRDA:333,0,1,31
BRDA:333,0,1,11
DA:334,28
BRDA:334,0,0,3
BRDA:334,0,1,28
DA:335,21
BRDA:335,0,0,21
DA:335,1
BRDA:335,0,0,1
BRDA:335,0,1,0
DA:336,10
DA:337,10
@ -182,9 +182,9 @@ BRDA:340,0,3,5
DA:343,11
BRDA:343,0,0,11
BRDA:343,0,1,0
DA:349,11
BRDA:349,0,0,10
BRDA:349,0,1,11
DA:349,22
BRDA:349,0,0,20
BRDA:349,0,1,22
DA:352,11
DA:353,10
BRDA:353,0,0,0
@ -218,5 +218,5 @@ DA:374,9
BRDA:374,0,0,1
BRDA:374,0,1,9
BRF:83
BRH:33
BRH:32
end_of_record

File diff suppressed because it is too large Load Diff

View File

@ -26,6 +26,5 @@ test.run(cmd=[
],
verilator_run=True)
test.files_identical(test.obj_dir + "/annotated/t_cover_line.v", "t/t_cover_line.out")
test.vcd_identical(test.trace_filename, test.golden_filename)
test.passes()

View File

@ -0,0 +1,18 @@
#!/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('simulator')
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,38 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 jalcim
// SPDX-License-Identifier: CC0-1.0
// Recursive constant function defined at file scope ($unit).
// Without the V3Scope.cpp fix, this triggers:
// %Error: Internal Error: V3Scope.cpp: No clone for package function
function automatic integer gate_depth;
input integer way;
integer d1, d2, sc, n1, n2;
begin
if (way <= 1) gate_depth = 0;
else if (way <= 4) gate_depth = 1;
else begin
sc = $clog2(way);
n1 = 1 << (sc - 1);
n2 = way - n1;
d1 = gate_depth(n1);
d2 = gate_depth(n2);
gate_depth = ((d1 > d2) ? d1 : d2) + 1;
end
end
endfunction
module t;
localparam D5 = gate_depth(5);
localparam D8 = gate_depth(8);
initial begin
if (D5 !== 2) $stop;
if (D8 !== 2) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -49,7 +49,7 @@
]},
{"type":"CFUNC","name":"_eval_initial__TOP","addr":"(QB)","loc":"d,11:8,11:9","slow":true,"scopep":"(Z)","argsp": [],
"varsp": [
{"type":"VAR","name":"__Vtemp_1","addr":"(RB)","loc":"d,49:120,49:121","dtypep":"(SB)","origName":"__Vtemp_1","verilogName":"__Vtemp_1","direction":"NONE","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
{"type":"VAR","name":"__Vtemp_1","addr":"(RB)","loc":"d,49:120,49:121","dtypep":"(SB)","origName":"__Vtemp_1","verilogName":"__Vtemp_1","direction":"NONE","noReset":true,"isFuncLocal":true,"lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
],
"stmtsp": [
{"type":"ASSIGN","name":"","addr":"(TB)","loc":"d,32:9,32:10","dtypep":"(UB)",
@ -868,9 +868,9 @@
"lhsp": [
{"type":"VARREF","name":"__Vdly__t.e","addr":"(XN)","loc":"d,24:9,24:10","dtypep":"(M)","access":"WR","varp":"(UN)","varScopep":"UNLINKED","classOrPackagep":"UNLINKED"}
],"timingControlp": []},
{"type":"VAR","name":"__Vtemp_1","addr":"(YN)","loc":"d,70:123,70:124","dtypep":"(SB)","origName":"__Vtemp_1","verilogName":"__Vtemp_1","direction":"NONE","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"__Vtemp_2","addr":"(ZN)","loc":"d,80:123,80:124","dtypep":"(SB)","origName":"__Vtemp_2","verilogName":"__Vtemp_2","direction":"NONE","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"__Vtemp_3","addr":"(AO)","loc":"d,90:123,90:124","dtypep":"(SB)","origName":"__Vtemp_3","verilogName":"__Vtemp_3","direction":"NONE","lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
{"type":"VAR","name":"__Vtemp_1","addr":"(YN)","loc":"d,70:123,70:124","dtypep":"(SB)","origName":"__Vtemp_1","verilogName":"__Vtemp_1","direction":"NONE","noReset":true,"isFuncLocal":true,"lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"__Vtemp_2","addr":"(ZN)","loc":"d,80:123,80:124","dtypep":"(SB)","origName":"__Vtemp_2","verilogName":"__Vtemp_2","direction":"NONE","noReset":true,"isFuncLocal":true,"lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []},
{"type":"VAR","name":"__Vtemp_3","addr":"(AO)","loc":"d,90:123,90:124","dtypep":"(SB)","origName":"__Vtemp_3","verilogName":"__Vtemp_3","direction":"NONE","noReset":true,"isFuncLocal":true,"lifetime":"NONE","varType":"STMTTEMP","dtypeName":"string","sensIfacep":"UNLINKED","childDTypep": [],"delayp": [],"valuep": [],"attrsp": []}
],
"stmtsp": [
{"type":"ASSIGN","name":"","addr":"(BO)","loc":"d,23:17,23:20","dtypep":"(S)",

View File

@ -0,0 +1,21 @@
#!/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('simulator')
if not test.have_solver:
test.skip("No constraint solver installed")
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,99 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// Test soft constraint solving per IEEE 1800-2017 section 18.5.13
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
`define check_range(gotv,minv,maxv) do if ((gotv) < (minv) || (gotv) > (maxv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d-%0d\n", `__FILE__,`__LINE__, (gotv), (minv), (maxv)); `stop; end while(0);
// verilog_format: on
// Case 1: Only soft, no hard -- soft should be satisfied
class Case1;
rand int x;
constraint c_soft { soft x == 5; }
endclass
// Case 2: Two soft on same var -- last-wins (c_b declared after c_a)
class Case2;
rand int x;
constraint c_a { soft x == 5; }
constraint c_b { soft x == 10; }
endclass
// Case 3: Soft on different vars -- both should be satisfied
class Case3;
rand int x;
rand int y;
constraint c_x { soft x == 7; }
constraint c_y { soft y == 3; }
endclass
// Case 4: Soft range partially covered by hard -- SAT at intersection
class Case4;
rand int x;
constraint c_soft { soft x inside {[1:10]}; }
constraint c_hard { x inside {[5:15]}; }
endclass
// Case 5: Soft completely overridden by hard -- hard wins
class Case5;
rand int x;
constraint c_soft { soft x == 5; }
constraint c_hard { x > 10; }
endclass
module t;
Case1 c1;
Case2 c2;
Case3 c3;
Case4 c4;
Case5 c5;
int rand_ok;
initial begin
c1 = new;
c2 = new;
c3 = new;
c4 = new;
c5 = new;
repeat (20) begin
// Case 1: only soft, no hard -- soft satisfied
rand_ok = c1.randomize();
`checkd(rand_ok, 1)
`checkd(c1.x, 5)
// Case 2: two soft on same var -- last-wins
rand_ok = c2.randomize();
`checkd(rand_ok, 1)
`checkd(c2.x, 10)
// Case 3: soft on different vars -- both satisfied
rand_ok = c3.randomize();
`checkd(rand_ok, 1)
`checkd(c3.x, 7)
`checkd(c3.y, 3)
// Case 4: soft range partially covered by hard -- intersection [5:10]
rand_ok = c4.randomize();
`checkd(rand_ok, 1)
`check_range(c4.x, 5, 10)
// Case 5: soft completely overridden by hard -- hard wins
rand_ok = c5.randomize();
`checkd(rand_ok, 1)
if (c5.x <= 10) begin
$write("%%Error: %s:%0d: x=%0d should be > 10\n", `__FILE__, `__LINE__, c5.x);
`stop;
end
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,21 @@
#!/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('simulator')
if not test.have_solver:
test.skip("No constraint solver installed")
test.compile()
test.execute()
test.passes()

View File

@ -0,0 +1,60 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// Test std::randomize() called from a static function referencing static
// class members in the 'with' clause.
// verilog_format: off
`define stop $stop
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t;
typedef enum bit [3:0] {
INSTR_ADD = 0,
INSTR_SUB = 1,
INSTR_MUL = 2,
INSTR_AND = 4
} instr_name_t;
class instr_base;
static instr_name_t allowed_instrs[$];
static function void init();
allowed_instrs.push_back(INSTR_ADD);
allowed_instrs.push_back(INSTR_SUB);
allowed_instrs.push_back(INSTR_MUL);
allowed_instrs.push_back(INSTR_AND);
endfunction
static function instr_name_t get_rand_instr();
instr_name_t name;
int ok;
ok = std::randomize(name) with {
name inside {allowed_instrs};
};
`checkd(ok, 1);
return name;
endfunction
endclass
initial begin
instr_name_t result;
instr_base::init();
repeat (20) begin
result = instr_base::get_rand_instr();
`checkd(result == INSTR_ADD || result == INSTR_SUB
|| result == INSTR_MUL || result == INSTR_AND, 1);
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule