2020-12-07 23:55:22 +01:00
|
|
|
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
|
|
|
|
//*************************************************************************
|
2024-07-12 16:18:18 +02:00
|
|
|
// DESCRIPTION: Verilator: Generate randomization procedures
|
2020-12-07 23:55:22 +01:00
|
|
|
//
|
|
|
|
|
// Code available from: https://verilator.org
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
//
|
2025-01-01 14:30:25 +01:00
|
|
|
// Copyright 2003-2025 by Wilson Snyder. This program is free software; you
|
2020-12-07 23:55:22 +01:00
|
|
|
// 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
|
|
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
// V3Randomize's Transformations:
|
|
|
|
|
//
|
|
|
|
|
// Each randomize() method call:
|
|
|
|
|
// Mark class of object on which randomize() is called
|
|
|
|
|
// Mark all classes that inherit from previously marked classed
|
|
|
|
|
// Mark all classes whose instances are randomized member variables of marked classes
|
|
|
|
|
// Each marked class:
|
2024-08-02 17:45:17 +02:00
|
|
|
// * define a virtual randomize() method that randomizes its random variables
|
|
|
|
|
// Each call to randomize():
|
|
|
|
|
// * define __Vrandwith### functions for randomize() calls with inline constraints and
|
|
|
|
|
// put then into randomized classes
|
|
|
|
|
// * replace calls to randomize() that use inline constraints with calls to __Vrandwith###
|
|
|
|
|
// functions
|
2020-12-07 23:55:22 +01:00
|
|
|
//
|
|
|
|
|
//*************************************************************************
|
|
|
|
|
|
2023-10-18 12:37:46 +02:00
|
|
|
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
#include "verilatedos.h"
|
|
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
#include "V3Randomize.h"
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
#include "V3Ast.h"
|
|
|
|
|
#include "V3Error.h"
|
|
|
|
|
#include "V3FileLine.h"
|
|
|
|
|
#include "V3Global.h"
|
2023-07-08 18:27:50 +02:00
|
|
|
#include "V3MemberMap.h"
|
2024-07-12 16:18:18 +02:00
|
|
|
#include "V3UniqueNames.h"
|
|
|
|
|
|
2024-08-02 17:45:17 +02:00
|
|
|
#include <queue>
|
|
|
|
|
#include <tuple>
|
2024-07-12 16:18:18 +02:00
|
|
|
#include <utility>
|
2022-08-05 11:56:57 +02:00
|
|
|
|
2022-09-18 21:53:42 +02:00
|
|
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
|
|
|
|
|
2024-08-13 20:20:31 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Determines if a class is used with randomization
|
|
|
|
|
|
|
|
|
|
enum ClassRandom : uint8_t {
|
|
|
|
|
NONE, // randomize() is not called
|
|
|
|
|
IS_RANDOMIZED, // randomize() is called
|
2025-10-19 11:57:28 +02:00
|
|
|
IS_RANDOMIZED_GLOBAL, // randomize() is called with global constraints
|
2024-08-13 20:20:31 +02:00
|
|
|
IS_RANDOMIZED_INLINE, // randomize() with args is called
|
2025-07-25 12:13:46 +02:00
|
|
|
IS_STD_RANDOMIZED, // std::randomize() is called
|
2024-08-13 20:20:31 +02:00
|
|
|
};
|
|
|
|
|
|
2025-10-09 20:31:04 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Constants for global constraint processing
|
|
|
|
|
|
|
|
|
|
static constexpr const char* GLOBAL_CONSTRAINT_SEPARATOR = "__DT__";
|
2025-10-22 10:18:02 +02:00
|
|
|
static constexpr const char* BASIC_RANDOMIZE_FUNC_NAME = "__VBasicRand";
|
2025-10-09 20:31:04 +02:00
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
// ######################################################################
|
|
|
|
|
// Establishes the target of a rand_mode() call
|
|
|
|
|
|
|
|
|
|
struct RandModeTarget final {
|
|
|
|
|
// MEMBERS
|
|
|
|
|
AstVar* const receiverp; // Variable that is the target of the rand_mode() call
|
|
|
|
|
AstNodeExpr* const fromp; // Expression from which the rand_mode() call originates
|
|
|
|
|
AstClass* const classp; // Class in which rand_mode should be set
|
|
|
|
|
|
|
|
|
|
// METHODS
|
|
|
|
|
static RandModeTarget get(AstNodeExpr* fromp, AstNodeModule* modp) {
|
|
|
|
|
if (!fromp) return {nullptr, nullptr, VN_AS(modp, Class)};
|
|
|
|
|
if (AstCMethodHard* const methodHardp = VN_CAST(fromp, CMethodHard)) {
|
|
|
|
|
return RandModeTarget::get(methodHardp->fromp(), modp);
|
|
|
|
|
}
|
|
|
|
|
AstVar* receiverp = nullptr;
|
2024-08-05 23:56:03 +02:00
|
|
|
AstClass* classp = VN_CAST(modp, Class);
|
2024-07-31 23:30:48 +02:00
|
|
|
if (AstVarRef* const varrefp = VN_CAST(fromp, VarRef)) {
|
|
|
|
|
receiverp = varrefp->varp();
|
2024-08-05 23:56:03 +02:00
|
|
|
if (receiverp->isRand()) {
|
|
|
|
|
fromp = nullptr;
|
|
|
|
|
if (receiverp->lifetime().isStatic()) {
|
|
|
|
|
classp = VN_AS(varrefp->classOrPackagep(), Class);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
} else if (AstMemberSel* const memberSelp = VN_CAST(fromp, MemberSel)) {
|
|
|
|
|
receiverp = memberSelp->varp();
|
2024-08-05 23:56:03 +02:00
|
|
|
if (receiverp->isRand()) {
|
|
|
|
|
fromp = memberSelp->fromp();
|
|
|
|
|
classp = VN_AS(fromp->dtypep()->skipRefp(), ClassRefDType)->classp();
|
|
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(receiverp, fromp, "Unknown rand_mode() receiver");
|
2024-08-05 23:56:03 +02:00
|
|
|
if (!receiverp->isRand()) {
|
2024-07-31 23:30:48 +02:00
|
|
|
AstClassRefDType* const classRefDtp
|
|
|
|
|
= VN_CAST(receiverp->dtypep()->skipRefp(), ClassRefDType);
|
2024-08-05 23:56:03 +02:00
|
|
|
if (classRefDtp) classp = classRefDtp->classp();
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-08-05 23:56:03 +02:00
|
|
|
return {receiverp, fromp, classp};
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// ######################################################################
|
2024-08-21 12:16:44 +02:00
|
|
|
// Stores info about a variable's rand_mode state/a constraint's constraint_mode state
|
2024-07-31 23:30:48 +02:00
|
|
|
|
2024-08-21 12:16:44 +02:00
|
|
|
union RandomizeMode final {
|
2024-07-31 23:30:48 +02:00
|
|
|
// MEMBERS
|
|
|
|
|
struct {
|
2024-08-21 12:16:44 +02:00
|
|
|
bool usesMode : 1; // Variable/constraint uses rand_mode/constraint_mode
|
|
|
|
|
uint32_t index : 31; // Index of var/constraint in rand_mode/constraint_mode vector
|
2024-07-31 23:30:48 +02:00
|
|
|
};
|
|
|
|
|
int asInt; // Representation as int to be stored in nodep->user*
|
|
|
|
|
};
|
|
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that marks classes needing a randomize() method
|
|
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
class RandomizeMarkVisitor final : public VNVisitor {
|
2020-12-07 23:55:22 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on Netlist
|
|
|
|
|
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
2025-07-25 12:13:46 +02:00
|
|
|
// AstNodeModule::user1() -> bool. Set true to indicate needs std::randomize processing
|
2024-05-17 16:38:34 +02:00
|
|
|
// AstNodeExpr::user1() -> bool. Set true to indicate constraint expression depending on a
|
|
|
|
|
// randomized variable
|
2024-07-31 23:30:48 +02:00
|
|
|
// AstVar::user1() -> bool. Set true to indicate needs rand_mode
|
2024-07-17 08:21:45 +02:00
|
|
|
// AstVar::user2p() -> AstNodeModule*. Pointer to containing module
|
2024-08-23 13:57:57 +02:00
|
|
|
// AstNodeFTask::user2p() -> AstNodeModule*. Pointer to containing module
|
2025-07-25 12:13:46 +02:00
|
|
|
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
2022-01-02 19:56:40 +01:00
|
|
|
const VNUser1InUse m_inuser1;
|
2024-07-17 08:21:45 +02:00
|
|
|
const VNUser2InUse m_inuser2;
|
2020-12-07 23:55:22 +01:00
|
|
|
|
2021-03-13 00:10:45 +01:00
|
|
|
using DerivedSet = std::unordered_set<AstClass*>;
|
2024-05-17 16:38:34 +02:00
|
|
|
using BaseToDerivedMap = std::unordered_map<const AstClass*, DerivedSet>;
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
BaseToDerivedMap m_baseToDerivedMap; // Mapping from base classes to classes that extend them
|
2023-05-07 00:33:08 +02:00
|
|
|
AstClass* m_classp = nullptr; // Current class
|
2024-11-09 18:45:55 +01:00
|
|
|
AstNode* m_constraintExprGenp = nullptr; // Current constraint or constraint if expression
|
2024-07-12 16:18:18 +02:00
|
|
|
AstNodeModule* m_modp; // Current module
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNodeStmt* m_stmtp = nullptr; // Current statement
|
2024-07-12 16:18:18 +02:00
|
|
|
std::set<AstNodeVarRef*> m_staticRefs; // References to static variables under `with` clauses
|
2025-10-19 11:55:59 +02:00
|
|
|
AstWith* m_withp = nullptr; // Current 'with' constraint node
|
|
|
|
|
std::vector<AstConstraint*> m_clonedConstraints; // List of cloned global constraints
|
2025-10-09 15:30:39 +02:00
|
|
|
std::unordered_set<const AstVar*> m_processedVars; // Track by variable instance, not class
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
2024-05-17 16:38:34 +02:00
|
|
|
void markMembers(const AstClass* nodep) {
|
|
|
|
|
for (const AstClass* classp = nodep; classp;
|
2020-12-07 23:55:22 +01:00
|
|
|
classp = classp->extendsp() ? classp->extendsp()->classp() : nullptr) {
|
2024-08-13 20:20:31 +02:00
|
|
|
for (AstNode* memberp = classp->stmtsp(); memberp; memberp = memberp->nextp()) {
|
|
|
|
|
AstVar* const varp = VN_CAST(memberp, Var);
|
|
|
|
|
if (!varp) continue;
|
|
|
|
|
// If member is randomizable and of class type, mark its class
|
|
|
|
|
if (varp->rand().isRandomizable()) {
|
2024-10-25 18:00:43 +02:00
|
|
|
const AstNodeDType* const varDtypep = varp->dtypep()->skipRefp();
|
|
|
|
|
const AstClassRefDType* classRefp = VN_CAST(varDtypep, ClassRefDType);
|
|
|
|
|
if (!classRefp) {
|
|
|
|
|
const AstNodeDType* subDTypep = varDtypep->subDTypep();
|
|
|
|
|
if (subDTypep) subDTypep = subDTypep->skipRefp();
|
|
|
|
|
classRefp = VN_CAST(subDTypep, ClassRefDType);
|
|
|
|
|
}
|
|
|
|
|
if (classRefp) {
|
2024-05-17 16:38:34 +02:00
|
|
|
AstClass* const rclassp = classRefp->classp();
|
2023-03-15 16:48:18 +01:00
|
|
|
if (!rclassp->user1()) {
|
2024-08-13 20:20:31 +02:00
|
|
|
rclassp->user1(IS_RANDOMIZED);
|
2023-03-15 16:48:18 +01:00
|
|
|
markMembers(rclassp);
|
|
|
|
|
markDerived(rclassp);
|
|
|
|
|
}
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
2024-08-13 20:20:31 +02:00
|
|
|
// If the class is randomized inline, all members use rand mode
|
|
|
|
|
if (nodep->user1() == IS_RANDOMIZED_INLINE) {
|
2024-08-21 12:16:44 +02:00
|
|
|
RandomizeMode randMode = {};
|
|
|
|
|
randMode.usesMode = true;
|
2024-08-13 20:20:31 +02:00
|
|
|
varp->user1(randMode.asInt);
|
|
|
|
|
}
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void markDerived(const AstClass* nodep) {
|
2020-12-07 23:55:22 +01:00
|
|
|
const auto it = m_baseToDerivedMap.find(nodep);
|
|
|
|
|
if (it != m_baseToDerivedMap.end()) {
|
|
|
|
|
for (auto* classp : it->second) {
|
2024-08-13 20:20:31 +02:00
|
|
|
if (classp->user1() < nodep->user1()) {
|
|
|
|
|
classp->user1(nodep->user1());
|
2023-03-15 16:48:18 +01:00
|
|
|
markMembers(classp);
|
|
|
|
|
markDerived(classp);
|
|
|
|
|
}
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void markAllDerived() {
|
2022-07-30 16:01:25 +02:00
|
|
|
for (const auto& p : m_baseToDerivedMap) {
|
2020-12-07 23:55:22 +01:00
|
|
|
if (p.first->user1()) markDerived(p.first);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
void setPackageRefs() {
|
|
|
|
|
for (AstNodeVarRef* staticRefp : m_staticRefs) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Updated classOrPackage ref for " << staticRefp->name());
|
2024-07-17 08:21:45 +02:00
|
|
|
staticRefp->classOrPackagep(VN_AS(staticRefp->varp()->user2p(), NodeModule));
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-10-22 10:18:02 +02:00
|
|
|
void markNestedGlobalConstrainedRecurse(AstNode* nodep) {
|
2025-10-26 20:03:16 +01:00
|
|
|
if (const AstVarRef* const refp = VN_CAST(nodep, VarRef)) {
|
|
|
|
|
AstVar* const varp = refp->varp();
|
2025-10-22 22:15:06 +02:00
|
|
|
if (varp->globalConstrained()) return;
|
|
|
|
|
varp->globalConstrained(true);
|
2025-10-26 20:03:16 +01:00
|
|
|
} else if (const AstMemberSel* const memberSelp = VN_CAST(nodep, MemberSel)) {
|
2025-10-19 11:55:59 +02:00
|
|
|
if (memberSelp->varp()) {
|
|
|
|
|
AstVar* const varp = memberSelp->varp();
|
2025-10-22 22:15:06 +02:00
|
|
|
if (varp->globalConstrained()) return;
|
|
|
|
|
varp->globalConstrained(true);
|
2025-10-19 11:55:59 +02:00
|
|
|
}
|
2025-10-22 10:18:02 +02:00
|
|
|
markNestedGlobalConstrainedRecurse(memberSelp->fromp());
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-09 16:02:59 +02:00
|
|
|
// Build MemberSel chain from variable path
|
2025-10-09 15:30:39 +02:00
|
|
|
AstNodeExpr* buildMemberSelChain(AstVarRef* rootVarRefp, const std::vector<AstVar*>& path) {
|
2025-10-20 22:52:06 +02:00
|
|
|
AstNodeExpr* exprp = rootVarRefp->cloneTree(false);
|
2025-10-09 15:30:39 +02:00
|
|
|
for (AstVar* memberVarp : path) {
|
2025-10-20 22:52:06 +02:00
|
|
|
AstMemberSel* memberSelp
|
|
|
|
|
= new AstMemberSel{rootVarRefp->fileline(), exprp, memberVarp};
|
|
|
|
|
memberSelp->user2p(m_classp);
|
|
|
|
|
exprp = memberSelp;
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
2025-10-20 22:52:06 +02:00
|
|
|
return exprp;
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
|
|
|
|
|
2025-10-22 10:18:02 +02:00
|
|
|
// Process a single constraint during nested constraint cloning
|
|
|
|
|
void processNestedConstraint(AstConstraint* const constrp, AstVarRef* rootVarRefp,
|
2025-10-22 10:19:15 +02:00
|
|
|
const std::vector<AstVar*>& newPath) {
|
2025-10-22 10:18:02 +02:00
|
|
|
std::string pathPrefix = rootVarRefp->name();
|
|
|
|
|
for (AstVar* pathMemberVarp : newPath) {
|
|
|
|
|
pathPrefix += GLOBAL_CONSTRAINT_SEPARATOR + pathMemberVarp->name();
|
|
|
|
|
}
|
|
|
|
|
|
2025-11-01 07:46:06 +01:00
|
|
|
const std::string newName = pathPrefix + GLOBAL_CONSTRAINT_SEPARATOR + constrp->name();
|
|
|
|
|
|
|
|
|
|
for (const AstConstraint* existingConstrp : m_clonedConstraints) {
|
|
|
|
|
if (existingConstrp->name() == newName) {
|
|
|
|
|
// Multiple paths lead to same constraint - unsupported pattern
|
|
|
|
|
std::string fullPath = rootVarRefp->name();
|
|
|
|
|
for (AstVar* pathVar : newPath) { fullPath += "." + pathVar->name(); }
|
|
|
|
|
constrp->v3warn(E_UNSUPPORTED, "Unsupported: One variable '"
|
|
|
|
|
<< fullPath
|
|
|
|
|
<< "' cannot have multiple global constraints");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstConstraint* const cloneConstrp = constrp->cloneTree(false);
|
|
|
|
|
cloneConstrp->name(newName);
|
2025-10-22 10:18:02 +02:00
|
|
|
cloneConstrp->foreach([&](AstVarRef* varRefp) {
|
|
|
|
|
AstNodeExpr* const chainp = buildMemberSelChain(rootVarRefp, newPath);
|
|
|
|
|
AstMemberSel* const finalSelp
|
|
|
|
|
= new AstMemberSel{varRefp->fileline(), chainp, varRefp->varp()};
|
|
|
|
|
finalSelp->user2p(m_classp);
|
|
|
|
|
varRefp->replaceWith(finalSelp);
|
|
|
|
|
VL_DO_DANGLING(varRefp->deleteTree(), varRefp);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
m_clonedConstraints.push_back(cloneConstrp);
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-09 20:31:04 +02:00
|
|
|
// Clone constraints from nested rand class members
|
2025-10-19 11:55:59 +02:00
|
|
|
void cloneNestedConstraintsRecurse(AstVarRef* rootVarRefp, AstClass* classp,
|
2025-10-19 11:57:28 +02:00
|
|
|
const std::vector<AstVar*>& pathToClass) {
|
2025-10-09 15:31:58 +02:00
|
|
|
for (AstNode* memberNodep = classp->membersp(); memberNodep;
|
|
|
|
|
memberNodep = memberNodep->nextp()) {
|
2025-10-22 22:15:06 +02:00
|
|
|
AstVar* const memberVarp = VN_CAST(memberNodep, Var);
|
|
|
|
|
if (!memberVarp) continue;
|
|
|
|
|
if (!memberVarp->rand().isRandomizable()) continue;
|
|
|
|
|
const AstClassRefDType* const memberClassRefp
|
|
|
|
|
= VN_CAST(memberVarp->dtypep()->skipRefp(), ClassRefDType);
|
|
|
|
|
if (!memberClassRefp || !memberClassRefp->classp()) continue;
|
|
|
|
|
|
|
|
|
|
AstClass* nestedClassp = memberClassRefp->classp();
|
|
|
|
|
|
|
|
|
|
std::vector<AstVar*> newPath = pathToClass;
|
|
|
|
|
newPath.push_back(memberVarp);
|
|
|
|
|
// Replace all variable references inside the cloned constraint with proper
|
|
|
|
|
// member selections
|
|
|
|
|
nestedClassp->foreachMember(
|
|
|
|
|
[&](AstClass* const containingClassp, AstConstraint* const constrp) {
|
|
|
|
|
processNestedConstraint(constrp, rootVarRefp, newPath);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cloneNestedConstraintsRecurse(rootVarRefp, nestedClassp, newPath);
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void cloneNestedConstraints(AstVarRef* rootVarRefp, AstClass* rootClass) {
|
|
|
|
|
std::vector<AstVar*> emptyPath;
|
2025-10-19 11:55:59 +02:00
|
|
|
cloneNestedConstraintsRecurse(rootVarRefp, rootClass, emptyPath);
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
|
|
|
|
|
2025-09-19 11:03:56 +02:00
|
|
|
void nameManipulation(AstVarRef* fromp, AstConstraint* cloneCons) {
|
|
|
|
|
cloneCons->name(fromp->name() + GLOBAL_CONSTRAINT_SEPARATOR + cloneCons->name());
|
|
|
|
|
cloneCons->foreach([&](AstVarRef* varRefp) {
|
2025-10-20 22:52:06 +02:00
|
|
|
AstVarRef* const clonedFromp = fromp->cloneTree(false);
|
|
|
|
|
AstMemberSel* const varMemberp
|
2025-09-19 16:13:39 +02:00
|
|
|
= new AstMemberSel{cloneCons->fileline(), clonedFromp, varRefp->varp()};
|
2025-09-19 11:03:56 +02:00
|
|
|
varMemberp->user2p(m_classp);
|
|
|
|
|
varRefp->replaceWith(varMemberp);
|
|
|
|
|
VL_DO_DANGLING(varRefp->deleteTree(), varRefp);
|
|
|
|
|
});
|
|
|
|
|
}
|
2025-10-24 10:57:21 +02:00
|
|
|
|
|
|
|
|
// Process a globally constrained variable by cloning its constraints
|
|
|
|
|
void processGlobalConstraint(AstVarRef* varRefp, AstClass* gConsClass) {
|
|
|
|
|
AstVar* const objVar = varRefp->varp();
|
|
|
|
|
|
|
|
|
|
// Process per-variable (object instance), not per-class
|
|
|
|
|
// This allows multiple objects of the same class (e.g., obj1 and obj2 of type Sub)
|
2025-10-26 20:03:16 +01:00
|
|
|
if (m_processedVars.insert(objVar).second) {
|
2025-10-24 10:57:21 +02:00
|
|
|
// Clone constraints from the top-level class (e.g., Level1 for obj_a)
|
|
|
|
|
gConsClass->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) {
|
|
|
|
|
AstConstraint* const cloneConstrp = constrp->cloneTree(false);
|
|
|
|
|
nameManipulation(varRefp, cloneConstrp);
|
|
|
|
|
m_clonedConstraints.push_back(cloneConstrp);
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
cloneNestedConstraints(varRefp, gConsClass);
|
|
|
|
|
}
|
|
|
|
|
}
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
// VISITORS
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2023-05-07 00:33:08 +02:00
|
|
|
VL_RESTORER(m_classp);
|
2024-07-17 08:21:45 +02:00
|
|
|
VL_RESTORER(m_modp);
|
2025-11-05 11:17:53 +01:00
|
|
|
VL_RESTORER(m_clonedConstraints);
|
2024-07-17 08:21:45 +02:00
|
|
|
m_modp = m_classp = nodep;
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateChildrenConst(nodep);
|
2020-12-07 23:55:22 +01:00
|
|
|
if (nodep->extendsp()) {
|
2025-10-27 15:06:38 +01:00
|
|
|
// Record derived class for inheritance hierarchy tracking
|
2024-05-17 16:38:34 +02:00
|
|
|
const AstClass* const basep = nodep->extendsp()->classp();
|
2020-12-07 23:55:22 +01:00
|
|
|
m_baseToDerivedMap[basep].insert(nodep);
|
|
|
|
|
}
|
2025-10-27 15:06:38 +01:00
|
|
|
for (AstConstraint* const constrp : m_clonedConstraints) m_classp->addStmtsp(constrp);
|
2025-10-31 14:34:15 +01:00
|
|
|
m_clonedConstraints.clear();
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
void visit(AstNodeStmt* nodep) override {
|
|
|
|
|
VL_RESTORER(m_stmtp);
|
|
|
|
|
m_stmtp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!nodep->backp()) VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2024-07-22 14:41:12 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
|
|
|
|
iterateChildrenConst(nodep);
|
2024-07-31 23:30:48 +02:00
|
|
|
if (nodep->name() == "rand_mode") {
|
|
|
|
|
AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall);
|
|
|
|
|
AstNodeExpr* const fromp = methodCallp ? methodCallp->fromp() : nullptr;
|
|
|
|
|
bool valid = true;
|
|
|
|
|
if (VN_IS(fromp, ArraySel)) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'rand_mode()' on unpacked array element");
|
|
|
|
|
valid = false;
|
2024-08-05 23:56:03 +02:00
|
|
|
} else if (VN_IS(fromp, StructSel)) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'rand_mode()' on unpacked struct element");
|
|
|
|
|
valid = false;
|
2024-07-31 23:30:48 +02:00
|
|
|
} else if (VN_IS(fromp, Sel)) {
|
|
|
|
|
nodep->v3error("Cannot call 'rand_mode()' on packed array element");
|
|
|
|
|
valid = false;
|
|
|
|
|
} else if (AstCMethodHard* const methodHardp = VN_CAST(fromp, CMethodHard)) {
|
2025-09-27 14:22:17 +02:00
|
|
|
if (methodHardp->method() == VCMethod::ARRAY_AT
|
|
|
|
|
|| methodHardp->method() == VCMethod::ARRAY_AT_WRITE) {
|
2024-07-31 23:30:48 +02:00
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'rand_mode()' on dynamic array element");
|
|
|
|
|
valid = false;
|
|
|
|
|
} else {
|
2025-03-24 00:51:54 +01:00
|
|
|
methodHardp->v3fatalSrc("Unknown rand_mode() receiver");
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-08-08 11:10:40 +02:00
|
|
|
if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr)
|
|
|
|
|
&& !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) {
|
2024-07-31 23:30:48 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
IGNOREDRETURN,
|
|
|
|
|
"Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)");
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (valid) {
|
|
|
|
|
const RandModeTarget randModeTarget = RandModeTarget::get(fromp, m_classp);
|
|
|
|
|
if ((!randModeTarget.receiverp || !randModeTarget.receiverp->isRand())
|
|
|
|
|
&& !nodep->pinsp()) {
|
|
|
|
|
nodep->v3error(
|
|
|
|
|
"Cannot call 'rand_mode()' as a function on non-random variable");
|
|
|
|
|
valid = false;
|
|
|
|
|
} else if (!randModeTarget.classp) {
|
|
|
|
|
nodep->v3error("Cannot call 'rand_mode()' on non-random, non-class variable");
|
|
|
|
|
valid = false;
|
|
|
|
|
} else if (nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
|
|
|
|
nodep->v3error("'rand_mode()' with arguments cannot be called as a function");
|
|
|
|
|
valid = false;
|
2024-08-05 23:56:03 +02:00
|
|
|
} else if (randModeTarget.receiverp
|
|
|
|
|
&& randModeTarget.receiverp->lifetime().isStatic()
|
|
|
|
|
&& randModeTarget.receiverp->isRand()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 'rand_mode()' on static variable");
|
|
|
|
|
valid = false;
|
2024-07-31 23:30:48 +02:00
|
|
|
} else if (randModeTarget.receiverp && randModeTarget.receiverp->isRand()) {
|
|
|
|
|
// Called on a rand member variable
|
2024-08-21 12:16:44 +02:00
|
|
|
RandomizeMode randMode = {};
|
|
|
|
|
randMode.usesMode = true;
|
2024-07-31 23:30:48 +02:00
|
|
|
randModeTarget.receiverp->user1(randMode.asInt);
|
|
|
|
|
} else {
|
|
|
|
|
// Called on 'this' or a non-rand class instance
|
|
|
|
|
randModeTarget.classp->foreachMember([&](AstClass*, AstVar* varp) {
|
|
|
|
|
if (!varp->isRand()) return;
|
2024-08-05 23:56:03 +02:00
|
|
|
if (varp->lifetime().isStatic()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'rand_mode()' on static variable: "
|
|
|
|
|
<< varp->prettyNameQ());
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
RandomizeMode randMode = {};
|
|
|
|
|
randMode.usesMode = true;
|
2024-07-31 23:30:48 +02:00
|
|
|
varp->user1(randMode.asInt);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!valid) {
|
|
|
|
|
if (!nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
|
|
|
|
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
} else {
|
|
|
|
|
m_stmtp->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
|
|
|
|
|
if (nodep->name() == "constraint_mode") {
|
|
|
|
|
bool valid = true;
|
|
|
|
|
if (nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
|
|
|
|
nodep->v3error(
|
|
|
|
|
"'constraint_mode()' with arguments cannot be called as a function");
|
|
|
|
|
valid = false;
|
2025-08-08 11:10:40 +02:00
|
|
|
} else if (!nodep->pinsp() && VN_IS(nodep->backp(), StmtExpr)
|
|
|
|
|
&& !nodep->backp()->fileline()->warnIsOff(V3ErrorCode::IGNOREDRETURN)) {
|
2024-08-21 12:16:44 +02:00
|
|
|
nodep->v3warn(
|
|
|
|
|
IGNOREDRETURN,
|
|
|
|
|
"Ignoring return value of non-void function (IEEE 1800-2023 13.4.1)");
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
AstConstraint* constrp = nullptr;
|
|
|
|
|
AstClass* classp = m_classp;
|
|
|
|
|
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
|
|
|
|
|
if (const AstConstraintRef* const constrRefp
|
|
|
|
|
= VN_CAST(methodCallp->fromp(), ConstraintRef)) {
|
|
|
|
|
constrp = constrRefp->constrp();
|
|
|
|
|
if (constrRefp->fromp()) classp = VN_AS(constrRefp->classOrPackagep(), Class);
|
|
|
|
|
if (constrp->isStatic()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'constraint_mode()' on static constraint");
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
} else if (AstClassRefDType* classRefDtp
|
|
|
|
|
= VN_CAST(methodCallp->fromp()->dtypep()->skipRefp(), ClassRefDType)) {
|
|
|
|
|
classp = classRefDtp->classp();
|
|
|
|
|
} else {
|
|
|
|
|
nodep->v3error("Cannot call 'constraint_mode()' on a non-class variable");
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (!nodep->pinsp() && !constrp) {
|
|
|
|
|
nodep->v3error("Cannot call 'constraint_mode()' as a function on a variable");
|
|
|
|
|
valid = false;
|
|
|
|
|
}
|
|
|
|
|
if (valid) {
|
|
|
|
|
RandomizeMode constraintMode = {};
|
|
|
|
|
constraintMode.usesMode = true;
|
|
|
|
|
if (constrp) {
|
|
|
|
|
constrp->user1(constraintMode.asInt);
|
|
|
|
|
} else {
|
|
|
|
|
classp->foreachMember([=](AstClass*, AstConstraint* constrp) {
|
|
|
|
|
if (constrp->isStatic()) {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: 'constraint_mode()' on static constraint: "
|
|
|
|
|
<< constrp->prettyNameQ());
|
|
|
|
|
}
|
|
|
|
|
constrp->user1(constraintMode.asInt);
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if (!nodep->pinsp() && !VN_IS(nodep->backp(), StmtExpr)) {
|
|
|
|
|
nodep->replaceWith(new AstConst{nodep->fileline(), 0});
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
} else {
|
|
|
|
|
m_stmtp->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
if (nodep->name() != "randomize") return;
|
2024-07-22 14:41:12 +02:00
|
|
|
AstClass* classp = m_classp;
|
|
|
|
|
if (const AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall)) {
|
|
|
|
|
if (const AstClassRefDType* const classRefp
|
|
|
|
|
= VN_CAST(methodCallp->fromp()->dtypep()->skipRefp(), ClassRefDType)) {
|
|
|
|
|
classp = classRefp->classp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (classp) {
|
2024-08-13 20:20:31 +02:00
|
|
|
if (!classp->user1()) classp->user1(IS_RANDOMIZED);
|
2020-12-07 23:55:22 +01:00
|
|
|
markMembers(classp);
|
|
|
|
|
}
|
2025-07-25 12:13:46 +02:00
|
|
|
if (nodep->classOrPackagep()->name() == "std") {
|
|
|
|
|
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
|
|
|
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
|
|
|
|
if (!argp) continue;
|
|
|
|
|
AstNodeExpr* exprp = argp->exprp();
|
|
|
|
|
while (exprp) {
|
|
|
|
|
AstVar* randVarp = nullptr;
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
|
|
|
randVarp = memberSelp->varp();
|
|
|
|
|
exprp = memberSelp->fromp();
|
|
|
|
|
} else {
|
|
|
|
|
AstVarRef* const varrefp = VN_AS(exprp, VarRef);
|
|
|
|
|
randVarp = varrefp->varp();
|
|
|
|
|
exprp = nullptr;
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
|
|
|
|
|
AstNode* backp = randVarp;
|
|
|
|
|
while (backp && (!VN_IS(backp, Class) && !VN_IS(backp, NodeModule))) {
|
|
|
|
|
backp = backp->backp();
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(VN_IS(backp, NodeModule), randVarp,
|
|
|
|
|
"No class or module found for rand variable");
|
|
|
|
|
backp->user1(IS_STD_RANDOMIZED);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-08-13 20:20:31 +02:00
|
|
|
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
|
|
|
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
|
|
|
|
if (!argp) continue;
|
|
|
|
|
classp->user1(IS_RANDOMIZED_INLINE);
|
|
|
|
|
AstNodeExpr* exprp = argp->exprp();
|
|
|
|
|
AstVar* fromVarp = nullptr; // If nodep is a method call, this is its receiver
|
|
|
|
|
if (AstMethodCall* methodCallp = VN_CAST(nodep, MethodCall)) {
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(methodCallp->fromp(), MemberSel)) {
|
|
|
|
|
fromVarp = memberSelp->varp();
|
|
|
|
|
} else {
|
|
|
|
|
AstVarRef* const varrefp = VN_AS(methodCallp->fromp(), VarRef);
|
|
|
|
|
fromVarp = varrefp->varp();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
while (exprp) {
|
|
|
|
|
AstVar* randVarp = nullptr;
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
|
|
|
randVarp = memberSelp->varp();
|
|
|
|
|
exprp = memberSelp->fromp();
|
|
|
|
|
} else {
|
|
|
|
|
AstVarRef* const varrefp = VN_AS(exprp, VarRef);
|
|
|
|
|
randVarp = varrefp->varp();
|
|
|
|
|
exprp = nullptr;
|
|
|
|
|
}
|
|
|
|
|
if (randVarp == fromVarp) break;
|
2025-03-08 20:11:12 +01:00
|
|
|
UASSERT_OBJ(randVarp, nodep, "No rand variable found");
|
2024-08-13 20:20:31 +02:00
|
|
|
AstNode* backp = randVarp;
|
|
|
|
|
while (backp && !VN_IS(backp, Class)) backp = backp->backp();
|
2024-08-21 12:16:44 +02:00
|
|
|
RandomizeMode randMode = {};
|
|
|
|
|
randMode.usesMode = true;
|
2024-08-13 20:20:31 +02:00
|
|
|
randVarp->user1(randMode.asInt);
|
|
|
|
|
VN_AS(backp, Class)->user1(IS_RANDOMIZED_INLINE);
|
|
|
|
|
}
|
|
|
|
|
}
|
2023-05-07 00:33:08 +02:00
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstConstraintExpr* nodep) override {
|
2024-11-09 18:45:55 +01:00
|
|
|
VL_RESTORER(m_constraintExprGenp);
|
|
|
|
|
m_constraintExprGenp = nodep;
|
2024-05-17 16:38:34 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2024-07-10 17:30:18 +02:00
|
|
|
void visit(AstConstraintIf* nodep) override {
|
|
|
|
|
{
|
2024-11-09 18:45:55 +01:00
|
|
|
VL_RESTORER(m_constraintExprGenp);
|
|
|
|
|
m_constraintExprGenp = nodep;
|
2024-07-10 17:30:18 +02:00
|
|
|
iterateConst(nodep->condp());
|
|
|
|
|
}
|
|
|
|
|
iterateAndNextConstNull(nodep->thensp());
|
|
|
|
|
iterateAndNextConstNull(nodep->elsesp());
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2024-11-09 18:45:55 +01:00
|
|
|
if (!m_constraintExprGenp) return;
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
if (nodep->varp()->lifetime().isStatic()) m_staticRefs.emplace(nodep);
|
|
|
|
|
|
2024-11-22 14:47:14 +01:00
|
|
|
if (nodep->varp()->rand().isRandomizable()) nodep->user1(true);
|
2024-05-17 16:38:34 +02:00
|
|
|
}
|
2024-08-02 17:45:17 +02:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2024-11-09 18:45:55 +01:00
|
|
|
if (!m_constraintExprGenp) return;
|
2024-10-02 16:29:47 +02:00
|
|
|
iterateChildrenConst(nodep);
|
2024-11-22 14:47:14 +01:00
|
|
|
// Member select are randomized when both object and member are marked as rand.
|
|
|
|
|
// Variable references in with clause are converted to member selects and their from() is
|
|
|
|
|
// of type AstLambdaArgRef. They are randomized too.
|
|
|
|
|
const bool randObject = nodep->fromp()->user1() || VN_IS(nodep->fromp(), LambdaArgRef);
|
|
|
|
|
nodep->user1(randObject && nodep->varp()->rand().isRandomizable());
|
2025-10-10 21:46:43 +02:00
|
|
|
|
2025-10-19 11:55:59 +02:00
|
|
|
if (m_withp) {
|
|
|
|
|
AstNode* backp = m_withp;
|
2025-09-19 11:03:56 +02:00
|
|
|
while (backp->backp()) {
|
2025-10-26 20:03:16 +01:00
|
|
|
if (const AstMethodCall* const callp = VN_CAST(backp, MethodCall)) {
|
|
|
|
|
AstClassRefDType* classdtype
|
|
|
|
|
= VN_AS(callp->fromp()->dtypep()->skipRefp(), ClassRefDType);
|
2025-09-19 11:03:56 +02:00
|
|
|
nodep->user2p(classdtype->classp());
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
backp = backp->backp();
|
|
|
|
|
}
|
2025-10-22 22:15:06 +02:00
|
|
|
} else {
|
2025-09-19 11:03:56 +02:00
|
|
|
nodep->user2p(m_modp);
|
2025-10-22 22:15:06 +02:00
|
|
|
}
|
2025-10-11 22:12:38 +02:00
|
|
|
if (randObject && nodep->varp()
|
|
|
|
|
&& nodep->varp()->rand().isRandomizable()) { // Process global constraints
|
2025-10-22 17:29:17 +02:00
|
|
|
if (m_classp && m_classp->user1() == IS_RANDOMIZED) {
|
2025-10-19 11:55:59 +02:00
|
|
|
m_classp->user1(IS_RANDOMIZED_GLOBAL);
|
2025-10-22 17:29:17 +02:00
|
|
|
}
|
2025-10-09 15:30:39 +02:00
|
|
|
// Mark the entire nested chain as participating in global constraints
|
|
|
|
|
if (VN_IS(nodep->fromp(), VarRef) || VN_IS(nodep->fromp(), MemberSel)) {
|
2025-10-22 10:18:02 +02:00
|
|
|
markNestedGlobalConstrainedRecurse(nodep->fromp());
|
2025-10-26 12:44:43 +01:00
|
|
|
} else if (VN_IS(nodep->fromp(), ArraySel)) {
|
2025-10-29 14:40:24 +01:00
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: " << nodep->prettyTypeName()
|
|
|
|
|
<< " within a global constraint");
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
2025-09-19 11:03:56 +02:00
|
|
|
// Global constraint processing algorithm:
|
|
|
|
|
// 1. Detect globally constrained object variables in randomized classes
|
|
|
|
|
// 2. Clone constraint trees from the constrained object's class
|
|
|
|
|
// 3. Rename cloned constraints with object prefix (obj.var format)
|
|
|
|
|
// 4. Insert cloned constraints into current class for solver processing
|
|
|
|
|
// 5. Use basic randomization for non-constrained variables to avoid recursion
|
|
|
|
|
|
2025-09-19 16:11:58 +02:00
|
|
|
// Extract and validate components early to avoid repeated type checks
|
|
|
|
|
AstVarRef* const varRefp = VN_CAST(nodep->fromp(), VarRef);
|
2025-10-26 20:03:16 +01:00
|
|
|
if (!varRefp) return;
|
2025-09-19 16:11:58 +02:00
|
|
|
|
2025-09-19 16:13:39 +02:00
|
|
|
const AstClassRefDType* const classRefp
|
2025-10-26 20:03:16 +01:00
|
|
|
= VN_AS(varRefp->dtypep()->skipRefp(), ClassRefDType);
|
2025-09-19 16:11:58 +02:00
|
|
|
|
2025-10-22 22:15:06 +02:00
|
|
|
if (nodep->user1() && varRefp->varp()->globalConstrained()) {
|
2025-10-24 10:57:21 +02:00
|
|
|
processGlobalConstraint(varRefp, classRefp->classp());
|
2025-09-19 11:03:56 +02:00
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 16:29:47 +02:00
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
|
|
|
|
VL_RESTORER(m_modp);
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2024-08-23 13:57:57 +02:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
|
|
|
|
nodep->user2p(m_modp);
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
void visit(AstVar* nodep) override {
|
2024-07-17 08:21:45 +02:00
|
|
|
nodep->user2p(m_modp);
|
2024-07-12 16:18:18 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2025-09-19 11:03:56 +02:00
|
|
|
void visit(AstWith* nodep) override {
|
2025-10-22 10:18:02 +02:00
|
|
|
VL_RESTORER(m_withp);
|
2025-10-19 11:55:59 +02:00
|
|
|
m_withp = nodep;
|
2025-09-19 11:03:56 +02:00
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
}
|
2022-11-12 03:53:05 +01:00
|
|
|
|
2024-11-22 14:47:14 +01:00
|
|
|
void visit(AstNodeExpr* nodep) override {
|
|
|
|
|
iterateChildrenConst(nodep);
|
|
|
|
|
if (!m_constraintExprGenp) return;
|
|
|
|
|
nodep->user1((nodep->op1p() && nodep->op1p()->user1())
|
|
|
|
|
|| (nodep->op2p() && nodep->op2p()->user1())
|
|
|
|
|
|| (nodep->op3p() && nodep->op3p()->user1())
|
|
|
|
|
|| (nodep->op4p() && nodep->op4p()->user1()));
|
|
|
|
|
}
|
2023-03-18 17:17:25 +01:00
|
|
|
void visit(AstNode* nodep) override { iterateChildrenConst(nodep); }
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2024-07-12 16:18:18 +02:00
|
|
|
explicit RandomizeMarkVisitor(AstNode* nodep) {
|
2023-03-18 17:17:25 +01:00
|
|
|
iterateConst(nodep);
|
2020-12-07 23:55:22 +01:00
|
|
|
markAllDerived();
|
2024-07-12 16:18:18 +02:00
|
|
|
setPackageRefs();
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~RandomizeMarkVisitor() override = default;
|
2020-12-07 23:55:22 +01:00
|
|
|
};
|
|
|
|
|
|
2024-05-17 16:38:34 +02:00
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that turns constraints into template strings for solvers
|
|
|
|
|
|
|
|
|
|
class ConstraintExprVisitor final : public VNVisitor {
|
|
|
|
|
// NODE STATE
|
2024-07-17 08:21:45 +02:00
|
|
|
// AstVar::user3() -> bool. Handled in constraints
|
2025-07-25 12:13:46 +02:00
|
|
|
// AstNodeExpr::user1() -> bool. Depending on a randomized variable
|
|
|
|
|
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
2024-07-17 08:21:45 +02:00
|
|
|
// VNuser3InUse m_inuser3; (Allocated for use in RandomizeVisitor)
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2024-07-17 08:21:45 +02:00
|
|
|
AstNodeFTask* const m_inlineInitTaskp; // Method to add write_var calls to
|
|
|
|
|
// (may be null, then new() is used)
|
2024-05-17 16:38:34 +02:00
|
|
|
AstVar* const m_genp; // VlRandomizer variable of the class
|
2024-07-31 23:30:48 +02:00
|
|
|
AstVar* m_randModeVarp; // Relevant randmode state variable
|
2024-07-10 17:30:18 +02:00
|
|
|
bool m_wantSingle = false; // Whether to merge constraint expressions with LOGAND
|
2024-07-17 08:21:45 +02:00
|
|
|
VMemberMap& m_memberMap; // Member names cached for fast lookup
|
2025-04-16 13:08:46 +02:00
|
|
|
bool m_structSel = false; // Marks when inside structSel
|
|
|
|
|
// (used to format "%@.%@" for struct arrays)
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2025-10-09 15:30:39 +02:00
|
|
|
// Build full path for a MemberSel chain (e.g., "obj.l2.l3.l4")
|
2025-10-23 16:23:31 +02:00
|
|
|
std::string buildMemberPath(const AstMemberSel* const memberSelp) {
|
2025-10-20 22:52:06 +02:00
|
|
|
const AstNode* fromp = memberSelp->fromp();
|
2025-10-26 12:44:43 +01:00
|
|
|
if (const AstVarRef* const refp = VN_CAST(fromp, VarRef)) {
|
2025-10-09 15:30:39 +02:00
|
|
|
// Base case: reached root VarRef
|
2025-10-26 12:44:43 +01:00
|
|
|
return refp->name() + "." + memberSelp->name();
|
|
|
|
|
} else if (const AstMemberSel* const selp = VN_CAST(fromp, MemberSel)) {
|
2025-10-09 15:30:39 +02:00
|
|
|
// Recursive case: build path from outer levels
|
2025-10-26 12:44:43 +01:00
|
|
|
return buildMemberPath(selp) + "." + memberSelp->name();
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
2025-10-22 16:29:30 +02:00
|
|
|
memberSelp->v3fatalSrc("Unexpected node type in MemberSel chain");
|
|
|
|
|
return "";
|
2025-10-09 15:30:39 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
AstSFormatF* getConstFormat(AstNodeExpr* nodep) {
|
|
|
|
|
return new AstSFormatF{nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false,
|
|
|
|
|
nodep};
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
bool editFormat(AstNodeExpr* nodep) {
|
|
|
|
|
if (nodep->user1()) return false;
|
|
|
|
|
// Replace computable expression with SMT constant
|
|
|
|
|
VNRelinker handle;
|
|
|
|
|
nodep->unlinkFrBack(&handle);
|
2024-07-31 23:30:48 +02:00
|
|
|
handle.relink(getConstFormat(nodep));
|
2024-05-17 16:38:34 +02:00
|
|
|
return true;
|
|
|
|
|
}
|
2024-07-10 17:30:18 +02:00
|
|
|
void editSMT(AstNodeExpr* nodep, AstNodeExpr* lhsp = nullptr, AstNodeExpr* rhsp = nullptr,
|
|
|
|
|
AstNodeExpr* thsp = nullptr) {
|
2024-05-17 16:38:34 +02:00
|
|
|
// Replace incomputable (result-dependent) expression with SMT expression
|
|
|
|
|
std::string smtExpr = nodep->emitSMT(); // Might need child width (AstExtend)
|
2025-07-28 16:14:03 +02:00
|
|
|
if (smtExpr == "") {
|
|
|
|
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported expression inside constraint");
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2025-06-24 17:59:09 +02:00
|
|
|
if (lhsp)
|
|
|
|
|
lhsp = VN_AS(iterateSubtreeReturnEdits(lhsp->backp() ? lhsp->unlinkFrBack() : lhsp),
|
|
|
|
|
NodeExpr);
|
|
|
|
|
if (rhsp)
|
|
|
|
|
rhsp = VN_AS(iterateSubtreeReturnEdits(rhsp->backp() ? rhsp->unlinkFrBack() : rhsp),
|
|
|
|
|
NodeExpr);
|
|
|
|
|
if (thsp)
|
|
|
|
|
thsp = VN_AS(iterateSubtreeReturnEdits(thsp->backp() ? thsp->unlinkFrBack() : thsp),
|
|
|
|
|
NodeExpr);
|
2024-05-17 16:38:34 +02:00
|
|
|
|
|
|
|
|
AstNodeExpr* argsp = nullptr;
|
|
|
|
|
for (string::iterator pos = smtExpr.begin(); pos != smtExpr.end(); ++pos) {
|
|
|
|
|
if (pos[0] == '%') {
|
|
|
|
|
++pos;
|
|
|
|
|
switch (pos[0]) {
|
|
|
|
|
case '%': break;
|
|
|
|
|
case 'l':
|
|
|
|
|
pos[0] = '@';
|
|
|
|
|
UASSERT_OBJ(lhsp, nodep, "emitSMT() references undef node");
|
|
|
|
|
argsp = AstNode::addNext(argsp, lhsp);
|
|
|
|
|
lhsp = nullptr;
|
|
|
|
|
break;
|
|
|
|
|
case 'r':
|
|
|
|
|
pos[0] = '@';
|
|
|
|
|
UASSERT_OBJ(rhsp, nodep, "emitSMT() references undef node");
|
|
|
|
|
argsp = AstNode::addNext(argsp, rhsp);
|
|
|
|
|
rhsp = nullptr;
|
|
|
|
|
break;
|
2024-07-10 17:30:18 +02:00
|
|
|
case 't':
|
|
|
|
|
pos[0] = '@';
|
|
|
|
|
UASSERT_OBJ(thsp, nodep, "emitSMT() references undef node");
|
|
|
|
|
argsp = AstNode::addNext(argsp, thsp);
|
|
|
|
|
thsp = nullptr;
|
|
|
|
|
break;
|
2024-05-17 16:38:34 +02:00
|
|
|
default: nodep->v3fatalSrc("Unknown emitSMT format code: %" << pos[0]); break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(!lhsp, nodep, "Missing emitSMT %l for " << lhsp);
|
|
|
|
|
UASSERT_OBJ(!rhsp, nodep, "Missing emitSMT %r for " << rhsp);
|
2024-07-10 17:30:18 +02:00
|
|
|
UASSERT_OBJ(!thsp, nodep, "Missing emitSMT %t for " << thsp);
|
2024-05-17 16:38:34 +02:00
|
|
|
AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp};
|
2025-04-16 13:08:46 +02:00
|
|
|
if (m_structSel && newp->name() == "(select %@ %@)") {
|
|
|
|
|
newp->name("%@.%@");
|
2025-05-07 13:07:16 +02:00
|
|
|
if (!VN_IS(nodep, AssocSel)) newp->exprsp()->nextp()->name("%x");
|
2025-04-16 13:08:46 +02:00
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-10 17:30:18 +02:00
|
|
|
AstNodeExpr* editSingle(FileLine* fl, AstNode* itemsp) {
|
|
|
|
|
if (!itemsp) return nullptr;
|
|
|
|
|
|
|
|
|
|
VL_RESTORER(m_wantSingle);
|
|
|
|
|
m_wantSingle = true;
|
|
|
|
|
|
|
|
|
|
{
|
|
|
|
|
AstBegin* const tempp
|
2025-09-23 20:49:01 +02:00
|
|
|
= new AstBegin{fl, "[EditWrapper]", itemsp->unlinkFrBackWithNext(), false};
|
2024-07-10 17:30:18 +02:00
|
|
|
VL_DO_DANGLING(iterateAndNextNull(tempp->stmtsp()), itemsp);
|
|
|
|
|
itemsp = tempp->stmtsp();
|
|
|
|
|
if (itemsp) itemsp->unlinkFrBackWithNext();
|
|
|
|
|
VL_DO_DANGLING(tempp->deleteTree(), tempp);
|
|
|
|
|
}
|
|
|
|
|
if (!itemsp) return nullptr;
|
|
|
|
|
|
|
|
|
|
AstNodeExpr* exprsp = VN_CAST(itemsp, NodeExpr);
|
|
|
|
|
UASSERT_OBJ(exprsp, itemsp, "Single not expression?");
|
|
|
|
|
|
|
|
|
|
if (!exprsp->nextp()) return exprsp;
|
|
|
|
|
|
|
|
|
|
std::ostringstream fmt;
|
2024-08-27 12:53:44 +02:00
|
|
|
fmt << "(bvand";
|
2024-07-10 17:30:18 +02:00
|
|
|
for (AstNode* itemp = exprsp; itemp; itemp = itemp->nextp()) fmt << " %@";
|
|
|
|
|
fmt << ')';
|
|
|
|
|
return new AstSFormatF{fl, fmt.str(), false, exprsp};
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-19 13:27:59 +02:00
|
|
|
AstNodeExpr* newSel(FileLine* fl, AstNodeExpr* arrayp, AstNodeExpr* idxp) {
|
|
|
|
|
// similar to V3WidthSel.cpp
|
|
|
|
|
AstNodeDType* const arrDtp = arrayp->unlinkFrBack()->dtypep();
|
|
|
|
|
AstNodeExpr* selp = nullptr;
|
|
|
|
|
if (VN_IS(arrDtp, QueueDType) || VN_IS(arrDtp, DynArrayDType))
|
2025-09-27 14:22:17 +02:00
|
|
|
selp = new AstCMethodHard{fl, arrayp, VCMethod::ARRAY_AT, idxp};
|
2024-09-19 13:27:59 +02:00
|
|
|
else if (VN_IS(arrDtp, UnpackArrayDType))
|
|
|
|
|
selp = new AstArraySel{fl, arrayp, idxp};
|
|
|
|
|
else if (VN_IS(arrDtp, AssocArrayDType))
|
|
|
|
|
selp = new AstAssocSel{fl, arrayp, idxp};
|
|
|
|
|
UASSERT_OBJ(selp, arrayp, "Selecting from non-array?");
|
|
|
|
|
selp->dtypep(arrDtp->subDTypep());
|
|
|
|
|
return selp;
|
|
|
|
|
}
|
|
|
|
|
|
2024-05-17 16:38:34 +02:00
|
|
|
// VISITORS
|
|
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
2025-07-25 12:13:46 +02:00
|
|
|
AstVar* varp = nodep->varp();
|
2024-11-15 16:45:06 +01:00
|
|
|
if (varp->user4p()) {
|
|
|
|
|
varp->user4p()->v3warn(
|
|
|
|
|
CONSTRAINTIGN,
|
|
|
|
|
"Size constraint combined with element constraint may not work correctly");
|
|
|
|
|
}
|
2025-10-10 21:46:43 +02:00
|
|
|
|
|
|
|
|
// Check if this variable is marked as globally constrained
|
2025-10-26 12:44:43 +01:00
|
|
|
const bool isGlobalConstrained = nodep->varp()->globalConstrained();
|
2025-10-10 21:46:43 +02:00
|
|
|
|
2025-10-09 15:30:39 +02:00
|
|
|
AstMemberSel* membersel = nullptr;
|
2025-10-10 21:46:43 +02:00
|
|
|
std::string smtName;
|
|
|
|
|
if (isGlobalConstrained && VN_IS(nodep->backp(), MemberSel)) {
|
|
|
|
|
// For global constraints: build complete path from topmost MemberSel
|
|
|
|
|
AstNode* topMemberSel = nodep->backp();
|
2025-10-09 15:30:39 +02:00
|
|
|
while (VN_IS(topMemberSel->backp(), MemberSel)) {
|
|
|
|
|
topMemberSel = topMemberSel->backp();
|
|
|
|
|
}
|
|
|
|
|
membersel = VN_AS(topMemberSel, MemberSel)->cloneTree(false);
|
2025-10-10 21:46:43 +02:00
|
|
|
smtName = buildMemberPath(membersel);
|
2025-10-09 15:30:39 +02:00
|
|
|
} else {
|
2025-10-10 21:46:43 +02:00
|
|
|
// No MemberSel: just variable name
|
|
|
|
|
smtName = nodep->name();
|
2025-09-28 11:10:36 +02:00
|
|
|
}
|
|
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
if (membersel) varp = membersel->varp();
|
2024-08-02 17:45:17 +02:00
|
|
|
AstNodeModule* const classOrPackagep = nodep->classOrPackagep();
|
2024-08-21 12:16:44 +02:00
|
|
|
const RandomizeMode randMode = {.asInt = varp->user1()};
|
|
|
|
|
if (!randMode.usesMode && editFormat(nodep)) return;
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
VNRelinker relinker;
|
|
|
|
|
nodep->unlinkFrBack(&relinker);
|
|
|
|
|
AstNodeExpr* exprp = new AstSFormatF{nodep->fileline(), smtName, false, nullptr};
|
2024-08-21 12:16:44 +02:00
|
|
|
if (randMode.usesMode) {
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNodeExpr* constFormatp = getConstFormat(nodep);
|
|
|
|
|
AstCMethodHard* const atp = new AstCMethodHard{
|
|
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{varp->fileline(), VN_AS(m_randModeVarp->user2p(), NodeModule),
|
|
|
|
|
m_randModeVarp, VAccess::READ},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::ARRAY_AT, new AstConst{nodep->fileline(), randMode.index}};
|
2024-07-31 23:30:48 +02:00
|
|
|
atp->dtypeSetUInt32();
|
|
|
|
|
exprp = new AstCond{varp->fileline(), atp, exprp, constFormatp};
|
2025-10-10 21:46:43 +02:00
|
|
|
} else if (!membersel || !isGlobalConstrained) {
|
|
|
|
|
// Only delete nodep here if it's not a global constraint
|
|
|
|
|
// Global constraints need nodep for write_var processing
|
2024-07-31 23:30:48 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
|
|
|
|
relinker.relink(exprp);
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2025-10-11 17:57:28 +02:00
|
|
|
// For global constraints: always call write_var with full path even if varp->user3() is
|
|
|
|
|
// set For normal constraints: only call write_var if varp->user3() is not set
|
2025-10-22 22:15:06 +02:00
|
|
|
if (!varp->user3() || (membersel && nodep->varp()->globalConstrained())) {
|
2025-10-10 21:46:43 +02:00
|
|
|
// For global constraints, delete nodep here after processing
|
2025-10-26 20:03:16 +01:00
|
|
|
if (membersel && isGlobalConstrained) VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
2024-05-17 16:38:34 +02:00
|
|
|
AstCMethodHard* const methodp = new AstCMethodHard{
|
2024-07-17 08:21:45 +02:00
|
|
|
varp->fileline(),
|
|
|
|
|
new AstVarRef{varp->fileline(), VN_AS(m_genp->user2p(), NodeModule), m_genp,
|
|
|
|
|
VAccess::READWRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::RANDOMIZER_WRITE_VAR};
|
2024-10-02 16:29:47 +02:00
|
|
|
uint32_t dimension = 0;
|
2024-11-08 20:04:58 +01:00
|
|
|
if (VN_IS(varp->dtypep(), UnpackArrayDType) || VN_IS(varp->dtypep(), DynArrayDType)
|
2024-12-12 17:31:54 +01:00
|
|
|
|| VN_IS(varp->dtypep(), QueueDType) || VN_IS(varp->dtypep(), AssocArrayDType)) {
|
2024-10-02 16:29:47 +02:00
|
|
|
const std::pair<uint32_t, uint32_t> dims
|
|
|
|
|
= varp->dtypep()->dimensions(/*includeBasic=*/true);
|
|
|
|
|
const uint32_t unpackedDimensions = dims.second;
|
|
|
|
|
dimension = unpackedDimensions;
|
|
|
|
|
}
|
2025-02-03 17:56:00 +01:00
|
|
|
if (VN_IS(varp->dtypeSkipRefp(), StructDType)
|
|
|
|
|
&& !VN_AS(varp->dtypeSkipRefp(), StructDType)->packed()) {
|
|
|
|
|
VN_AS(varp->dtypeSkipRefp(), StructDType)->markConstrainedRand(true);
|
|
|
|
|
dimension = 1;
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
methodp->dtypeSetVoid();
|
2025-07-25 12:13:46 +02:00
|
|
|
AstClass* const classp
|
|
|
|
|
= membersel ? VN_AS(membersel->user2p(), Class) : VN_AS(varp->user2p(), Class);
|
2025-10-19 11:55:59 +02:00
|
|
|
if (membersel) {
|
|
|
|
|
methodp->addPinsp(membersel);
|
|
|
|
|
} else {
|
2025-10-26 20:03:16 +01:00
|
|
|
AstVarRef* const varRefp
|
|
|
|
|
= new AstVarRef{varp->fileline(), classp, varp, VAccess::WRITE};
|
|
|
|
|
varRefp->classOrPackagep(classOrPackagep);
|
2025-10-19 11:55:59 +02:00
|
|
|
methodp->addPinsp(varRefp);
|
|
|
|
|
}
|
2024-11-08 20:04:58 +01:00
|
|
|
AstNodeDType* tmpDtypep = varp->dtypep();
|
|
|
|
|
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|
2024-12-12 17:31:54 +01:00
|
|
|
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
|
2024-11-08 20:04:58 +01:00
|
|
|
tmpDtypep = tmpDtypep->subDTypep();
|
2025-02-18 07:14:51 +01:00
|
|
|
const size_t width = tmpDtypep->width();
|
2024-09-10 15:33:14 +02:00
|
|
|
methodp->addPinsp(
|
|
|
|
|
new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, width});
|
2024-05-17 16:38:34 +02:00
|
|
|
AstNodeExpr* const varnamep
|
|
|
|
|
= new AstCExpr{varp->fileline(), "\"" + smtName + "\"", varp->width()};
|
|
|
|
|
varnamep->dtypep(varp->dtypep());
|
|
|
|
|
methodp->addPinsp(varnamep);
|
2024-10-02 16:29:47 +02:00
|
|
|
methodp->addPinsp(
|
|
|
|
|
new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, dimension});
|
2024-08-21 12:16:44 +02:00
|
|
|
if (randMode.usesMode) {
|
2024-07-31 23:30:48 +02:00
|
|
|
methodp->addPinsp(
|
|
|
|
|
new AstConst{varp->fileline(), AstConst::Unsized64{}, randMode.index});
|
|
|
|
|
}
|
2024-07-17 08:21:45 +02:00
|
|
|
AstNodeFTask* initTaskp = m_inlineInitTaskp;
|
|
|
|
|
if (!initTaskp) {
|
|
|
|
|
varp->user3(true); // Mark as set up in new()
|
|
|
|
|
initTaskp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
|
|
|
|
UASSERT_OBJ(initTaskp, classp, "No new() in class");
|
|
|
|
|
}
|
|
|
|
|
initTaskp->addStmtsp(methodp->makeStmt());
|
2024-05-17 16:38:34 +02:00
|
|
|
}
|
|
|
|
|
}
|
2025-07-28 16:14:03 +02:00
|
|
|
void visit(AstCountOnes* nodep) override {
|
|
|
|
|
// Convert it to (x & 1) + ((x & 2) >> 1) + ((x & 4) >> 2) + ...
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack();
|
|
|
|
|
V3Number numOne{nodep, argp->width(), 1};
|
|
|
|
|
AstNodeExpr* sump = new AstAnd{fl, argp, new AstConst{fl, numOne}};
|
|
|
|
|
sump->user1(true);
|
|
|
|
|
for (int i = 1; i < argp->width(); i++) {
|
|
|
|
|
V3Number numBitMask{nodep, argp->width(), 0};
|
|
|
|
|
numBitMask.setBit(i, 1);
|
|
|
|
|
AstAnd* const andp
|
|
|
|
|
= new AstAnd{fl, argp->cloneTreePure(false), new AstConst{fl, numBitMask}};
|
|
|
|
|
andp->user1(true);
|
|
|
|
|
AstShiftR* const shiftp = new AstShiftR{
|
|
|
|
|
fl, andp, new AstConst{fl, AstConst::WidthedValue{}, argp->width(), (uint32_t)i}};
|
|
|
|
|
shiftp->user1(true);
|
|
|
|
|
shiftp->dtypeFrom(nodep);
|
|
|
|
|
sump = new AstAdd{nodep->fileline(), sump, shiftp};
|
|
|
|
|
sump->user1(true);
|
|
|
|
|
}
|
|
|
|
|
// Restore the original width
|
|
|
|
|
if (nodep->width() > sump->width()) {
|
|
|
|
|
sump = new AstExtend{fl, sump, nodep->width()};
|
|
|
|
|
sump->user1(true);
|
|
|
|
|
} else if (nodep->width() < sump->width()) {
|
|
|
|
|
sump = new AstSel{fl, sump, 0, nodep->width()};
|
|
|
|
|
sump->user1(true);
|
|
|
|
|
}
|
|
|
|
|
nodep->replaceWith(sump);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
iterate(sump);
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstNodeBiop* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
editSMT(nodep, nodep->lhsp(), nodep->rhsp());
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeUniop* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
editSMT(nodep, nodep->lhsp());
|
|
|
|
|
}
|
2024-07-10 17:30:18 +02:00
|
|
|
void visit(AstNodeTriop* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
editSMT(nodep, nodep->lhsp(), nodep->rhsp(), nodep->thsp());
|
|
|
|
|
}
|
2025-08-16 00:49:06 +02:00
|
|
|
void visit(AstCond* nodep) override {
|
2024-07-10 17:30:18 +02:00
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
if (!nodep->condp()->user1()) {
|
|
|
|
|
// Do not burden the solver if cond computable: (cond ? "then" : "else")
|
|
|
|
|
iterate(nodep->thenp());
|
|
|
|
|
iterate(nodep->elsep());
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Fall back to "(ite cond then else)"
|
|
|
|
|
visit(static_cast<AstNodeTriop*>(nodep));
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstReplicate* nodep) override {
|
|
|
|
|
// Biop, but RHS is harmful
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
editSMT(nodep, nodep->srcp());
|
|
|
|
|
}
|
2024-08-08 16:37:08 +02:00
|
|
|
void visit(AstSel* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
VNRelinker handle;
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
2025-06-24 17:59:09 +02:00
|
|
|
AstNodeExpr* const msbp = new AstSFormatF{
|
|
|
|
|
fl, "%1d", false,
|
|
|
|
|
new AstAdd{fl, nodep->lsbp()->cloneTreePure(false),
|
|
|
|
|
new AstConst{fl, static_cast<uint32_t>(nodep->widthConst() - 1)}}};
|
2024-08-08 16:37:08 +02:00
|
|
|
AstNodeExpr* const lsbp
|
|
|
|
|
= new AstSFormatF{fl, "%1d", false, nodep->lsbp()->unlinkFrBack(&handle)};
|
|
|
|
|
handle.relink(lsbp);
|
|
|
|
|
|
|
|
|
|
editSMT(nodep, nodep->fromp(), lsbp, msbp);
|
|
|
|
|
}
|
2025-02-03 17:56:00 +01:00
|
|
|
void visit(AstStructSel* nodep) override {
|
2025-04-16 13:08:46 +02:00
|
|
|
m_structSel = true;
|
2025-02-03 17:56:00 +01:00
|
|
|
if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) {
|
2025-02-24 23:51:51 +01:00
|
|
|
AstNodeExpr* const fromp = nodep->fromp();
|
|
|
|
|
if (VN_IS(fromp, StructSel)) {
|
|
|
|
|
VN_AS(fromp->dtypep()->skipRefp(), StructDType)->markConstrainedRand(true);
|
|
|
|
|
}
|
|
|
|
|
AstMemberDType* memberp = VN_AS(fromp->dtypep()->skipRefp(), StructDType)->membersp();
|
|
|
|
|
while (memberp) {
|
2025-02-03 17:56:00 +01:00
|
|
|
if (memberp->name() == nodep->name()) {
|
|
|
|
|
memberp->markConstrainedRand(true);
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
memberp = VN_CAST(memberp->nextp(), MemberDType);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-04-16 13:08:46 +02:00
|
|
|
// Mark Random for structArray
|
2025-05-07 13:07:16 +02:00
|
|
|
if (VN_IS(nodep->fromp(), ArraySel) || VN_IS(nodep->fromp(), CMethodHard)) {
|
|
|
|
|
AstNodeExpr* const fromp = VN_IS(nodep->fromp(), ArraySel)
|
|
|
|
|
? VN_AS(nodep->fromp(), ArraySel)->fromp()
|
|
|
|
|
: VN_AS(nodep->fromp(), CMethodHard)->fromp();
|
2025-04-16 13:08:46 +02:00
|
|
|
AstStructDType* const dtypep
|
|
|
|
|
= VN_AS(fromp->dtypep()->skipRefp()->subDTypep()->skipRefp(), StructDType);
|
|
|
|
|
dtypep->markConstrainedRand(true);
|
|
|
|
|
AstMemberDType* memberp = dtypep->membersp();
|
|
|
|
|
while (memberp) {
|
|
|
|
|
if (memberp->name() == nodep->name()) {
|
|
|
|
|
memberp->markConstrainedRand(true);
|
|
|
|
|
break;
|
|
|
|
|
} else
|
|
|
|
|
memberp = VN_CAST(memberp->nextp(), MemberDType);
|
|
|
|
|
}
|
|
|
|
|
}
|
2025-02-03 17:56:00 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
2025-04-16 13:08:46 +02:00
|
|
|
AstSFormatF* newp = nullptr;
|
|
|
|
|
if (VN_AS(nodep->fromp(), SFormatF)->name() == "%@.%@") {
|
|
|
|
|
newp = new AstSFormatF{fl, "%@.%@." + nodep->name(), false,
|
|
|
|
|
VN_AS(nodep->fromp(), SFormatF)->exprsp()->cloneTreePure(true)};
|
2025-05-07 13:07:16 +02:00
|
|
|
if (newp->exprsp()->nextp()->name().rfind("#x", 0) == 0)
|
|
|
|
|
newp->exprsp()->nextp()->name("%x"); // for #x%x to %x
|
2025-04-16 13:08:46 +02:00
|
|
|
} else {
|
|
|
|
|
newp = new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false,
|
|
|
|
|
nullptr};
|
|
|
|
|
}
|
|
|
|
|
m_structSel = false;
|
2025-02-03 17:56:00 +01:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2024-12-12 17:31:54 +01:00
|
|
|
void visit(AstAssocSel* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
2025-05-07 13:07:16 +02:00
|
|
|
// Adaptive formatting and type handling for associative array keys
|
2025-03-11 18:32:34 +01:00
|
|
|
if (VN_IS(nodep->bitp(), VarRef) && VN_AS(nodep->bitp(), VarRef)->isString()) {
|
|
|
|
|
VNRelinker handle;
|
2025-05-07 13:07:16 +02:00
|
|
|
AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32p" : "#x%32p"), false,
|
|
|
|
|
nodep->bitp()->unlinkFrBack(&handle)};
|
2025-03-11 18:32:34 +01:00
|
|
|
handle.relink(idxp);
|
|
|
|
|
editSMT(nodep, nodep->fromp(), idxp);
|
|
|
|
|
} else if (VN_IS(nodep->bitp(), CvtPackString)
|
|
|
|
|
&& VN_IS(nodep->bitp()->dtypep(), BasicDType)) {
|
2024-12-12 17:31:54 +01:00
|
|
|
AstCvtPackString* const stringp = VN_AS(nodep->bitp(), CvtPackString);
|
2025-01-09 14:33:38 +01:00
|
|
|
const size_t stringSize = VN_AS(stringp->lhsp(), Const)->width();
|
|
|
|
|
if (stringSize > 128) {
|
|
|
|
|
stringp->v3warn(
|
|
|
|
|
CONSTRAINTIGN,
|
|
|
|
|
"Unsupported: Constrained randomization of associative array keys of "
|
|
|
|
|
<< stringSize << "bits, limit is 128 bits");
|
|
|
|
|
}
|
2024-12-12 17:31:54 +01:00
|
|
|
VNRelinker handle;
|
2025-05-07 13:07:16 +02:00
|
|
|
AstNodeExpr* const idxp = new AstSFormatF{fl, (m_structSel ? "%32x" : "#x%32x"), false,
|
|
|
|
|
stringp->lhsp()->unlinkFrBack(&handle)};
|
2025-01-09 14:33:38 +01:00
|
|
|
handle.relink(idxp);
|
|
|
|
|
editSMT(nodep, nodep->fromp(), idxp);
|
2024-12-12 17:31:54 +01:00
|
|
|
} else {
|
2025-01-11 18:07:52 +01:00
|
|
|
if (VN_IS(nodep->bitp()->dtypep(), BasicDType)
|
|
|
|
|
|| (VN_IS(nodep->bitp()->dtypep(), StructDType)
|
|
|
|
|
&& VN_AS(nodep->bitp()->dtypep(), StructDType)->packed())
|
|
|
|
|
|| VN_IS(nodep->bitp()->dtypep(), EnumDType)
|
|
|
|
|
|| VN_IS(nodep->bitp()->dtypep(), PackArrayDType)) {
|
|
|
|
|
VNRelinker handle;
|
|
|
|
|
const int actual_width = nodep->bitp()->width();
|
|
|
|
|
std::string fmt;
|
|
|
|
|
// Normalize to standard bit width
|
|
|
|
|
if (actual_width <= 8) {
|
2025-05-07 13:07:16 +02:00
|
|
|
fmt = m_structSel ? "%2x" : "#x%2x";
|
2025-01-11 18:07:52 +01:00
|
|
|
} else if (actual_width <= 16) {
|
2025-05-07 13:07:16 +02:00
|
|
|
fmt = m_structSel ? "%4x" : "#x%4x";
|
2025-01-11 18:07:52 +01:00
|
|
|
} else {
|
2025-05-07 13:07:16 +02:00
|
|
|
fmt = (m_structSel ? "%" : "#x%")
|
|
|
|
|
+ std::to_string(VL_WORDS_I(actual_width) * 8) + "x";
|
2025-01-11 18:07:52 +01:00
|
|
|
}
|
|
|
|
|
AstNodeExpr* const idxp
|
|
|
|
|
= new AstSFormatF{fl, fmt, false, nodep->bitp()->unlinkFrBack(&handle)};
|
|
|
|
|
handle.relink(idxp);
|
|
|
|
|
editSMT(nodep, nodep->fromp(), idxp);
|
2024-12-12 17:31:54 +01:00
|
|
|
} else {
|
2025-01-11 18:07:52 +01:00
|
|
|
nodep->bitp()->v3error(
|
|
|
|
|
"Illegal non-integral expression or subexpression in random constraint."
|
|
|
|
|
" (IEEE 1800-2023 18.3)");
|
2024-12-12 17:31:54 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-10-02 16:29:47 +02:00
|
|
|
void visit(AstArraySel* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
VNRelinker handle;
|
|
|
|
|
AstNodeExpr* const indexp
|
|
|
|
|
= new AstSFormatF{fl, "#x%8x", false, nodep->bitp()->unlinkFrBack(&handle)};
|
|
|
|
|
handle.relink(indexp);
|
|
|
|
|
editSMT(nodep, nodep->fromp(), indexp);
|
|
|
|
|
}
|
2024-11-22 14:47:14 +01:00
|
|
|
void visit(AstMemberSel* nodep) override {
|
2025-10-11 23:24:14 +02:00
|
|
|
if (nodep->varp()->rand().isRandomizable() && nodep->fromp()) {
|
2025-10-09 15:30:39 +02:00
|
|
|
AstNode* rootNode = nodep->fromp();
|
2025-10-26 20:04:30 +01:00
|
|
|
while (const AstMemberSel* const selp = VN_CAST(rootNode, MemberSel))
|
|
|
|
|
rootNode = selp->fromp();
|
2025-10-09 15:30:39 +02:00
|
|
|
// Check if the root variable participates in global constraints
|
2025-10-26 20:03:16 +01:00
|
|
|
if (const AstVarRef* const varRefp = VN_CAST(rootNode, VarRef)) {
|
|
|
|
|
AstVar* const constrainedVar = varRefp->varp();
|
|
|
|
|
if (constrainedVar->globalConstrained()) {
|
2025-10-24 10:57:21 +02:00
|
|
|
// Global constraint - unwrap the MemberSel
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
nodep->replaceWith(nodep->fromp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2025-09-19 11:03:56 +02:00
|
|
|
}
|
2024-11-22 14:47:14 +01:00
|
|
|
}
|
2025-11-04 14:28:42 +01:00
|
|
|
// Handle MemberSel references created by captureRefByThis()
|
|
|
|
|
if (VN_IS(nodep->fromp(), VarRef)
|
|
|
|
|
&& nodep->fromp()->user1() // Depending on a randomized variable
|
|
|
|
|
&& nodep->user2p() // Pointer to containing module
|
|
|
|
|
&& VN_AS(nodep->user2p(), NodeModule) == nodep->varp()->user2p()) {
|
|
|
|
|
// Convert to VarRef
|
|
|
|
|
AstVarRef* const varRefp
|
|
|
|
|
= new AstVarRef{nodep->fileline(), nodep->varp(), VAccess::READ};
|
|
|
|
|
varRefp->user1(nodep->varp()->rand().isRandomizable());
|
|
|
|
|
varRefp->classOrPackagep(VN_AS(nodep->user2p(), NodeModule));
|
|
|
|
|
nodep->replaceWith(varRefp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
visit(varRefp);
|
|
|
|
|
} else {
|
|
|
|
|
editFormat(nodep);
|
|
|
|
|
}
|
2024-11-22 14:47:14 +01:00
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstSFormatF* nodep) override {}
|
2024-07-10 17:30:18 +02:00
|
|
|
void visit(AstStmtExpr* nodep) override {}
|
|
|
|
|
void visit(AstConstraintIf* nodep) override {
|
|
|
|
|
AstNodeExpr* newp = nullptr;
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstNodeExpr* const thenp = editSingle(fl, nodep->thensp());
|
|
|
|
|
AstNodeExpr* const elsep = editSingle(fl, nodep->elsesp());
|
|
|
|
|
if (thenp && elsep) {
|
|
|
|
|
newp = new AstCond{fl, nodep->condp()->unlinkFrBack(), thenp, elsep};
|
|
|
|
|
} else if (thenp) {
|
|
|
|
|
newp = new AstLogIf{fl, nodep->condp()->unlinkFrBack(), thenp};
|
|
|
|
|
} else if (elsep) {
|
|
|
|
|
newp = new AstLogIf{fl, new AstNot{fl, nodep->condp()->unlinkFrBack()}, elsep};
|
|
|
|
|
}
|
|
|
|
|
if (newp) {
|
|
|
|
|
newp->user1(true); // Assume result-dependent
|
|
|
|
|
nodep->replaceWith(new AstConstraintExpr{fl, newp});
|
|
|
|
|
} else {
|
|
|
|
|
nodep->unlinkFrBack();
|
|
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2025-09-23 20:49:01 +02:00
|
|
|
void visit(AstGenBlock* nodep) override {
|
|
|
|
|
// Dubious but this is what we used to do. Does that mean no randomzie
|
|
|
|
|
// methods work under a generage block?
|
|
|
|
|
}
|
2024-09-10 13:17:21 +02:00
|
|
|
void visit(AstBegin* nodep) override {}
|
2024-07-10 17:30:18 +02:00
|
|
|
void visit(AstConstraintForeach* nodep) override {
|
2024-08-02 16:03:55 +02:00
|
|
|
// Convert to plain foreach
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstNode* const arrayp = nodep->arrayp()->unlinkFrBack();
|
2024-08-02 16:03:55 +02:00
|
|
|
if (m_wantSingle) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstNodeExpr* const itemp = editSingle(fl, nodep->stmtsp());
|
|
|
|
|
AstCStmt* const cstmtp = new AstCStmt{fl};
|
|
|
|
|
cstmtp->add("ret += \" \";\n");
|
|
|
|
|
cstmtp->add("ret += ");
|
|
|
|
|
cstmtp->add(itemp);
|
|
|
|
|
cstmtp->add(";");
|
|
|
|
|
AstCExpr* const cexprp = new AstCExpr{fl};
|
|
|
|
|
cexprp->dtypeSetString();
|
|
|
|
|
cexprp->add("([&]{\nstd::string ret;\n");
|
|
|
|
|
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
|
|
|
|
|
cexprp->add("return ret.empty() ? \"#b1\" : \"(bvand\" + ret + \")\";\n})()");
|
|
|
|
|
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
|
2024-08-02 16:03:55 +02:00
|
|
|
} else {
|
|
|
|
|
iterateAndNextNull(nodep->stmtsp());
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
nodep->replaceWith(new AstBegin{
|
|
|
|
|
fl, "", new AstForeach{fl, arrayp, nodep->stmtsp()->unlinkFrBackWithNext()},
|
|
|
|
|
true});
|
2024-08-02 16:03:55 +02:00
|
|
|
}
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
2024-07-10 17:30:18 +02:00
|
|
|
}
|
|
|
|
|
void visit(AstConstraintBefore* nodep) override {
|
2024-09-12 14:20:36 +02:00
|
|
|
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (imperfect distribution)");
|
2024-07-10 17:30:18 +02:00
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstConstraintUnique* nodep) override {
|
|
|
|
|
nodep->v3warn(CONSTRAINTIGN, "Constraint expression ignored (unsupported)");
|
|
|
|
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstConstraintExpr* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (m_wantSingle) {
|
|
|
|
|
nodep->replaceWith(nodep->exprp()->unlinkFrBack());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
// Only hard constraints are currently supported
|
|
|
|
|
AstCMethodHard* const callp = new AstCMethodHard{
|
2024-07-17 08:21:45 +02:00
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(), VN_AS(m_genp->user2p(), NodeModule), m_genp,
|
|
|
|
|
VAccess::READWRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::RANDOMIZER_HARD, nodep->exprp()->unlinkFrBack()};
|
2024-07-10 17:30:18 +02:00
|
|
|
callp->dtypeSetVoid();
|
|
|
|
|
nodep->replaceWith(callp->makeStmt());
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
void visit(AstCMethodHard* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
2024-09-19 13:27:59 +02:00
|
|
|
FileLine* const fl = nodep->fileline();
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2025-09-27 14:22:17 +02:00
|
|
|
if (nodep->method() == VCMethod::ARRAY_AT && nodep->fromp()->user1()) {
|
2024-09-10 15:33:14 +02:00
|
|
|
iterateChildren(nodep);
|
2025-04-16 13:08:46 +02:00
|
|
|
AstNodeExpr* pinp = nodep->pinsp()->unlinkFrBack();
|
|
|
|
|
if (VN_IS(pinp, SFormatF) && m_structSel) VN_AS(pinp, SFormatF)->name("%x");
|
|
|
|
|
AstNodeExpr* const argsp = AstNode::addNext(nodep->fromp()->unlinkFrBack(), pinp);
|
|
|
|
|
AstSFormatF* newp = nullptr;
|
|
|
|
|
if (m_structSel)
|
|
|
|
|
newp = new AstSFormatF{fl, "%@.%@", false, argsp};
|
|
|
|
|
else
|
|
|
|
|
newp = new AstSFormatF{fl, "(select %@ %@)", false, argsp};
|
2024-09-10 15:33:14 +02:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2025-09-27 14:22:17 +02:00
|
|
|
if (nodep->method() == VCMethod::ARRAY_INSIDE) {
|
2024-09-19 13:27:59 +02:00
|
|
|
bool randArr = nodep->fromp()->user1();
|
|
|
|
|
|
|
|
|
|
AstVar* const newVarp
|
|
|
|
|
= new AstVar{fl, VVarType::BLOCKTEMP, "__Vinside", nodep->findSigned32DType()};
|
|
|
|
|
AstNodeExpr* const idxRefp = new AstVarRef{nodep->fileline(), newVarp, VAccess::READ};
|
|
|
|
|
AstSelLoopVars* const arrayp
|
|
|
|
|
= new AstSelLoopVars{fl, nodep->fromp()->cloneTreePure(false), newVarp};
|
|
|
|
|
AstNodeExpr* const selp = newSel(nodep->fileline(), nodep->fromp(), idxRefp);
|
|
|
|
|
selp->user1(randArr);
|
|
|
|
|
AstNode* const itemp = new AstEq{fl, selp, nodep->pinsp()->unlinkFrBack()};
|
|
|
|
|
itemp->user1(true);
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
|
|
|
|
|
AstCStmt* const cstmtp = new AstCStmt{fl};
|
|
|
|
|
cstmtp->add("ret += \" \";\n");
|
|
|
|
|
cstmtp->add("ret += ");
|
|
|
|
|
cstmtp->add(iterateSubtreeReturnEdits(itemp));
|
|
|
|
|
cstmtp->add(";");
|
|
|
|
|
AstCExpr* const cexprp = new AstCExpr{fl};
|
|
|
|
|
cexprp->dtypeSetString();
|
|
|
|
|
cexprp->add("([&]{\nstd::string ret;\n");
|
|
|
|
|
cexprp->add(new AstBegin{fl, "", new AstForeach{fl, arrayp, cstmtp}, true});
|
|
|
|
|
cexprp->add("return ret.empty() ? \"#b0\" : \"(bvor\" + ret + \")\";\n})()");
|
|
|
|
|
nodep->replaceWith(new AstSFormatF{fl, "%@", false, cexprp});
|
2024-09-19 13:27:59 +02:00
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-09-10 15:33:14 +02:00
|
|
|
nodep->v3warn(CONSTRAINTIGN,
|
|
|
|
|
"Unsupported: randomizing this expression, treating as state");
|
|
|
|
|
nodep->user1(false);
|
2024-05-17 16:38:34 +02:00
|
|
|
|
2024-09-10 15:33:14 +02:00
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
nodep->v3fatalSrc("Method not handled in constraints? " << nodep);
|
2024-05-17 16:38:34 +02:00
|
|
|
}
|
|
|
|
|
void visit(AstNodeExpr* nodep) override {
|
|
|
|
|
if (editFormat(nodep)) return;
|
|
|
|
|
nodep->v3fatalSrc(
|
|
|
|
|
"Visit function missing? Constraint function missing for math node: " << nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNode* nodep) override {
|
|
|
|
|
nodep->v3fatalSrc(
|
|
|
|
|
"Visit function missing? Constraint function missing for node: " << nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2024-07-17 08:21:45 +02:00
|
|
|
explicit ConstraintExprVisitor(VMemberMap& memberMap, AstNode* nodep,
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNodeFTask* inlineInitTaskp, AstVar* genp,
|
|
|
|
|
AstVar* randModeVarp)
|
2024-07-17 08:21:45 +02:00
|
|
|
: m_inlineInitTaskp{inlineInitTaskp}
|
|
|
|
|
, m_genp{genp}
|
2024-07-31 23:30:48 +02:00
|
|
|
, m_randModeVarp{randModeVarp}
|
2024-07-17 08:21:45 +02:00
|
|
|
, m_memberMap{memberMap} {
|
2024-07-10 17:30:18 +02:00
|
|
|
iterateAndNextNull(nodep);
|
2024-05-17 16:38:34 +02:00
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-02 17:45:17 +02:00
|
|
|
enum class CaptureMode : uint8_t {
|
|
|
|
|
CAP_NO = 0x0,
|
|
|
|
|
CAP_VALUE = 0x01,
|
|
|
|
|
CAP_THIS = 0x02,
|
|
|
|
|
CAP_F_SET_CLASSORPACKAGEP = 0x4,
|
|
|
|
|
CAP_F_XREF = 0x8
|
|
|
|
|
};
|
|
|
|
|
CaptureMode operator|(CaptureMode a, CaptureMode b) {
|
|
|
|
|
return static_cast<CaptureMode>(static_cast<uint8_t>(a) | static_cast<uint8_t>(b));
|
|
|
|
|
}
|
|
|
|
|
CaptureMode operator&(CaptureMode a, CaptureMode b) {
|
|
|
|
|
return static_cast<CaptureMode>(static_cast<uint8_t>(a) & static_cast<uint8_t>(b));
|
|
|
|
|
}
|
|
|
|
|
CaptureMode mode(CaptureMode a) { return a & static_cast<CaptureMode>(0x3); }
|
|
|
|
|
bool hasFlags(CaptureMode a, CaptureMode flags) {
|
|
|
|
|
return ((static_cast<uint8_t>(a) & 0xc & static_cast<uint8_t>(flags))
|
|
|
|
|
== static_cast<uint8_t>(flags));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
class CaptureVisitor final : public VNVisitor {
|
2024-07-12 16:18:18 +02:00
|
|
|
AstArg* m_argsp; // Original references turned into arguments
|
2024-08-02 17:45:17 +02:00
|
|
|
AstNodeModule* m_callerp; // Module of the outer context (for capturing `this`)
|
2024-08-20 19:25:58 +02:00
|
|
|
AstClass* m_targetp; // Module of inner context (for symbol lookup)
|
2024-08-02 17:45:17 +02:00
|
|
|
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
|
|
|
|
|
|
|
|
|
|
// METHODS
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
bool captureVariable(FileLine* const fileline, AstNodeVarRef* varrefp, AstVar*& varp) {
|
|
|
|
|
auto it = m_varCloneMap.find(varrefp->varp());
|
|
|
|
|
if (it == m_varCloneMap.end()) {
|
|
|
|
|
AstVar* const newVarp = varrefp->varp()->cloneTree(false);
|
|
|
|
|
newVarp->fileline(fileline);
|
|
|
|
|
newVarp->varType(VVarType::BLOCKTEMP);
|
|
|
|
|
newVarp->funcLocal(true);
|
|
|
|
|
newVarp->direction(VDirection::INPUT);
|
2025-09-22 01:52:19 +02:00
|
|
|
newVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
m_varCloneMap.emplace(varrefp->varp(), newVarp);
|
|
|
|
|
varp = newVarp;
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
varp = it->second;
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_Node>
|
|
|
|
|
void fixupClassOrPackage(AstNode* memberp, T_Node refp) {
|
2024-08-23 13:57:57 +02:00
|
|
|
AstNodeModule* const declClassp = VN_AS(memberp->user2p(), NodeModule);
|
2024-08-20 19:25:58 +02:00
|
|
|
if (declClassp != m_targetp) refp->classOrPackagep(declClassp);
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-11-30 02:20:38 +01:00
|
|
|
template <typename T_Node>
|
|
|
|
|
bool isReferenceToInnerMember(T_Node nodep) {
|
2024-08-02 17:45:17 +02:00
|
|
|
return VN_IS(nodep->fromp(), LambdaArgRef);
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-08-02 17:45:17 +02:00
|
|
|
AstVar* importThisp(FileLine* fl) {
|
|
|
|
|
if (!m_thisp) {
|
|
|
|
|
AstClassRefDType* const refDTypep
|
|
|
|
|
= new AstClassRefDType{fl, VN_AS(m_callerp, Class), nullptr};
|
|
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(refDTypep);
|
|
|
|
|
m_thisp = new AstVar{fl, VVarType::BLOCKTEMP, "__Vthis", refDTypep};
|
|
|
|
|
m_thisp->funcLocal(true);
|
2025-09-22 01:52:19 +02:00
|
|
|
m_thisp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2024-08-02 17:45:17 +02:00
|
|
|
m_thisp->direction(VDirection::INPUT);
|
|
|
|
|
m_argsp = AstNode::addNext(m_argsp, new AstArg{fl, "", new AstThisRef{fl, refDTypep}});
|
|
|
|
|
}
|
|
|
|
|
return m_thisp;
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
AstVar* getVar(AstVar* const varp) const {
|
|
|
|
|
const auto it = m_varCloneMap.find(varp);
|
2025-03-20 03:55:11 +01:00
|
|
|
if (it == m_varCloneMap.end()) return nullptr;
|
2024-07-12 16:18:18 +02:00
|
|
|
return it->second;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-02 17:45:17 +02:00
|
|
|
CaptureMode getVarRefCaptureMode(AstNodeVarRef* varRefp) {
|
2024-08-23 13:57:57 +02:00
|
|
|
AstNodeModule* const varModp = VN_AS(varRefp->varp()->user2p(), NodeModule);
|
|
|
|
|
AstClass* const varClassp = VN_CAST(varModp, Class);
|
|
|
|
|
AstClass* const callerClassp = VN_CAST(m_callerp, Class);
|
2024-08-02 17:45:17 +02:00
|
|
|
|
2024-08-23 13:57:57 +02:00
|
|
|
const bool callerIsClass = callerClassp;
|
2024-08-02 17:45:17 +02:00
|
|
|
const bool refIsXref = VN_IS(varRefp, VarXRef);
|
|
|
|
|
const bool varIsFuncLocal = varRefp->varp()->isFuncLocal();
|
|
|
|
|
const bool varHasAutomaticLifetime = varRefp->varp()->lifetime().isAutomatic();
|
2024-08-23 13:57:57 +02:00
|
|
|
const bool varIsFieldOfCaller = AstClass::isClassExtendedFrom(callerClassp, varClassp);
|
2024-08-29 23:02:21 +02:00
|
|
|
const bool varIsParam = varRefp->varp()->isParam();
|
2024-09-26 19:31:06 +02:00
|
|
|
const bool varIsConstraintIterator
|
|
|
|
|
= VN_IS(varRefp->varp()->firstAbovep(), SelLoopVars)
|
|
|
|
|
&& VN_IS(varRefp->varp()->firstAbovep()->firstAbovep(), ConstraintForeach);
|
2024-08-02 17:45:17 +02:00
|
|
|
if (refIsXref) return CaptureMode::CAP_VALUE | CaptureMode::CAP_F_XREF;
|
2024-09-26 19:31:06 +02:00
|
|
|
if (varIsConstraintIterator) return CaptureMode::CAP_NO;
|
2024-08-02 17:45:17 +02:00
|
|
|
if (varIsFuncLocal && varHasAutomaticLifetime) return CaptureMode::CAP_VALUE;
|
2024-08-29 23:02:21 +02:00
|
|
|
if (varIsParam) return CaptureMode::CAP_VALUE;
|
2024-08-02 17:45:17 +02:00
|
|
|
// Static var in function (will not be inlined, because it's in class)
|
|
|
|
|
if (callerIsClass && varIsFuncLocal) return CaptureMode::CAP_VALUE;
|
|
|
|
|
if (callerIsClass && varIsFieldOfCaller) return CaptureMode::CAP_THIS;
|
|
|
|
|
UASSERT_OBJ(!callerIsClass, varRefp, "Invalid reference?");
|
|
|
|
|
return CaptureMode::CAP_VALUE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void captureRefByValue(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
|
|
|
|
|
AstVar* newVarp;
|
|
|
|
|
bool newCapture = captureVariable(nodep->fileline(), nodep, newVarp /*ref*/);
|
|
|
|
|
AstNodeVarRef* const newVarRefp = newCapture ? nodep->cloneTree(false) : nullptr;
|
|
|
|
|
if (!hasFlags(capModeFlags, CaptureMode::CAP_F_SET_CLASSORPACKAGEP)) {
|
|
|
|
|
// Keeping classOrPackagep will cause a broken link after inlining
|
|
|
|
|
nodep->classOrPackagep(nullptr); // AstScope will figure this out
|
|
|
|
|
}
|
|
|
|
|
nodep->varp(newVarp);
|
|
|
|
|
if (!newCapture) return;
|
|
|
|
|
if (hasFlags(capModeFlags, CaptureMode::CAP_F_XREF)) {
|
|
|
|
|
AstVarRef* const notXVarRefp
|
|
|
|
|
= new AstVarRef{nodep->fileline(), newVarp, VAccess::READ};
|
|
|
|
|
notXVarRefp->classOrPackagep(nodep->classOrPackagep());
|
|
|
|
|
nodep->replaceWith(notXVarRefp);
|
|
|
|
|
nodep->deleteTree();
|
|
|
|
|
nodep = notXVarRefp;
|
|
|
|
|
}
|
|
|
|
|
m_ignore.emplace(nodep);
|
|
|
|
|
m_argsp = AstNode::addNext(m_argsp, new AstArg{nodep->fileline(), "", newVarRefp});
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
void captureRefByThis(AstNodeVarRef* nodep, CaptureMode capModeFlags) {
|
|
|
|
|
AstVar* const thisp = importThisp(nodep->fileline());
|
|
|
|
|
AstVarRef* const thisRefp = new AstVarRef{nodep->fileline(), thisp, nodep->access()};
|
2025-07-25 12:13:46 +02:00
|
|
|
thisRefp->user1(true);
|
2024-08-02 17:45:17 +02:00
|
|
|
m_ignore.emplace(thisRefp);
|
|
|
|
|
AstMemberSel* const memberSelp
|
2025-08-09 00:21:12 +02:00
|
|
|
= new AstMemberSel{nodep->fileline(), thisRefp, nodep->varp()};
|
2025-07-25 12:13:46 +02:00
|
|
|
memberSelp->user2p(m_targetp);
|
2024-08-02 17:45:17 +02:00
|
|
|
nodep->replaceWith(memberSelp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
m_ignore.emplace(memberSelp);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// VISITORS
|
|
|
|
|
|
|
|
|
|
void visit(AstNodeVarRef* nodep) override {
|
|
|
|
|
if (m_ignore.count(nodep)) return;
|
|
|
|
|
m_ignore.emplace(nodep);
|
|
|
|
|
UASSERT_OBJ(nodep->varp(), nodep, "Variable unlinked");
|
|
|
|
|
CaptureMode capMode = getVarRefCaptureMode(nodep);
|
|
|
|
|
if (mode(capMode) == CaptureMode::CAP_NO) return;
|
|
|
|
|
if (mode(capMode) == CaptureMode::CAP_VALUE) captureRefByValue(nodep, capMode);
|
|
|
|
|
if (mode(capMode) == CaptureMode::CAP_THIS) captureRefByThis(nodep, capMode);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
|
|
|
|
if (m_ignore.count(nodep)) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
m_ignore.emplace(nodep);
|
|
|
|
|
UASSERT_OBJ(nodep->taskp(), nodep, "Task unlinked");
|
|
|
|
|
// We assume that constraint targets are not referenced this way.
|
|
|
|
|
if (VN_IS(nodep, MethodCall) || VN_IS(nodep, New)) {
|
|
|
|
|
m_ignore.emplace(nodep);
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-08-23 13:57:57 +02:00
|
|
|
AstClass* classp = VN_CAST(nodep->taskp()->user2p(), Class);
|
2024-08-02 17:45:17 +02:00
|
|
|
if ((classp == m_callerp) && VN_IS(m_callerp, Class)) {
|
|
|
|
|
AstNodeExpr* const pinsp = nodep->pinsp();
|
|
|
|
|
if (pinsp) pinsp->unlinkFrBack();
|
|
|
|
|
AstVar* const thisp = importThisp(nodep->fileline());
|
|
|
|
|
AstVarRef* const thisRefp = new AstVarRef{
|
|
|
|
|
nodep->fileline(), thisp, nodep->isPure() ? VAccess::READ : VAccess::READWRITE};
|
|
|
|
|
m_ignore.emplace(thisRefp);
|
|
|
|
|
AstMethodCall* const methodCallp
|
|
|
|
|
= new AstMethodCall{nodep->fileline(), thisRefp, thisp->name(), pinsp};
|
|
|
|
|
methodCallp->taskp(nodep->taskp());
|
|
|
|
|
methodCallp->dtypep(nodep->dtypep());
|
|
|
|
|
nodep->replaceWith(methodCallp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
m_ignore.emplace(methodCallp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
void visit(AstMemberSel* nodep) override {
|
|
|
|
|
if (!isReferenceToInnerMember(nodep)) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstVarRef* const varRefp
|
2025-08-09 00:21:12 +02:00
|
|
|
= new AstVarRef{nodep->fileline(), nodep->varp(), nodep->access()};
|
2024-08-02 17:45:17 +02:00
|
|
|
fixupClassOrPackage(nodep->varp(), varRefp);
|
|
|
|
|
varRefp->user1(nodep->user1());
|
|
|
|
|
nodep->replaceWith(varRefp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
m_ignore.emplace(varRefp);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstMethodCall* nodep) override {
|
|
|
|
|
if (!isReferenceToInnerMember(nodep) || m_ignore.count(nodep)) {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* const pinsp
|
|
|
|
|
= nodep->pinsp() ? nodep->pinsp()->unlinkFrBackWithNext() : nullptr;
|
|
|
|
|
AstNodeFTaskRef* taskRefp = nullptr;
|
2024-12-15 15:15:49 +01:00
|
|
|
if (AstTask* const taskp = VN_CAST(nodep->taskp(), Task))
|
|
|
|
|
taskRefp = new AstTaskRef{nodep->fileline(), taskp, pinsp};
|
|
|
|
|
else if (AstFunc* const taskp = VN_CAST(nodep->taskp(), Func))
|
|
|
|
|
taskRefp = new AstFuncRef{nodep->fileline(), taskp, pinsp};
|
2024-08-02 17:45:17 +02:00
|
|
|
UASSERT_OBJ(taskRefp, nodep, "Node needs to point to regular method");
|
|
|
|
|
fixupClassOrPackage(nodep->taskp(), taskRefp);
|
|
|
|
|
taskRefp->user1(nodep->user1());
|
|
|
|
|
nodep->replaceWith(taskRefp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
m_ignore.emplace(taskRefp);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
|
|
|
|
|
|
|
|
|
public:
|
2024-08-20 19:25:58 +02:00
|
|
|
explicit CaptureVisitor(AstNode* const nodep, AstNodeModule* callerp, AstClass* const targetp)
|
2025-07-04 00:59:32 +02:00
|
|
|
: m_argsp{nullptr}
|
|
|
|
|
, m_callerp{callerp}
|
|
|
|
|
, m_targetp{targetp} {
|
2024-08-02 17:45:17 +02:00
|
|
|
iterateAndNextNull(nodep);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// PUBLIC METHODS
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstArg* getArgs() const { return m_argsp; }
|
2024-08-02 17:45:17 +02:00
|
|
|
|
|
|
|
|
void addFunctionArguments(AstNodeFTask* funcp) const {
|
|
|
|
|
for (AstArg* argp = getArgs(); argp; argp = VN_AS(argp->nextp(), Arg)) {
|
|
|
|
|
if (AstNodeVarRef* varrefp = VN_CAST(argp->exprp(), NodeVarRef)) {
|
|
|
|
|
if ((varrefp->classOrPackagep() == m_callerp) || VN_IS(varrefp, VarXRef)) {
|
|
|
|
|
// Keeping classOrPackagep will cause a broken link after inlining
|
|
|
|
|
varrefp->classOrPackagep(nullptr);
|
|
|
|
|
}
|
|
|
|
|
funcp->addStmtsp(getVar(varrefp->varp()));
|
|
|
|
|
} else {
|
|
|
|
|
UASSERT_OBJ(VN_IS(argp->exprp(), ThisRef), argp->exprp(), "Wrong arg expression");
|
|
|
|
|
funcp->addStmtsp(m_thisp);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
};
|
|
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
//######################################################################
|
|
|
|
|
// Visitor that defines a randomize method where needed
|
|
|
|
|
|
2022-01-02 19:56:40 +01:00
|
|
|
class RandomizeVisitor final : public VNVisitor {
|
2020-12-07 23:55:22 +01:00
|
|
|
// NODE STATE
|
|
|
|
|
// Cleared on Netlist
|
|
|
|
|
// AstClass::user1() -> bool. Set true to indicate needs randomize processing
|
2024-08-23 13:57:57 +02:00
|
|
|
// AstVar::user2p() -> AstNodeModule*. Pointer to containing module
|
|
|
|
|
// AstNodeFTask::user2p() -> AstNodeModule*. Pointer to containing module
|
2020-12-07 23:55:22 +01:00
|
|
|
// AstEnumDType::user2() -> AstVar*. Pointer to table with enum values
|
2024-07-12 16:18:18 +02:00
|
|
|
// AstConstraint::user2p() -> AstTask*. Pointer to constraint setup procedure
|
2024-08-21 12:16:44 +02:00
|
|
|
// AstClass::user2p() -> AstVar*. Rand mode state variable
|
2024-07-17 08:21:45 +02:00
|
|
|
// AstVar::user3() -> bool. Handled in constraints
|
|
|
|
|
// AstClass::user3p() -> AstVar*. Constrained randomizer variable
|
2024-11-15 16:45:06 +01:00
|
|
|
// AstConstraint::user3p() -> AstTask*. Pointer to resize procedure
|
2024-08-21 12:16:44 +02:00
|
|
|
// AstClass::user4p() -> AstVar*. Constraint mode state variable
|
2024-11-15 16:45:06 +01:00
|
|
|
// AstVar::user4p() -> AstVar*. Size variable for constrained queues
|
2025-07-25 12:13:46 +02:00
|
|
|
// AstMemberSel::user2p() -> AstNodeModule*. Pointer to containing module
|
2022-01-02 19:56:40 +01:00
|
|
|
// VNUser1InUse m_inuser1; (Allocated for use in RandomizeMarkVisitor)
|
2024-07-17 08:21:45 +02:00
|
|
|
// VNUser2InUse m_inuser2; (Allocated for use in RandomizeMarkVisitor)
|
2024-05-17 16:38:34 +02:00
|
|
|
const VNUser3InUse m_inuser3;
|
2024-07-31 23:30:48 +02:00
|
|
|
const VNUser4InUse m_inuser4;
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
// STATE
|
2024-07-12 16:18:18 +02:00
|
|
|
V3UniqueNames m_inlineUniqueNames; // For generating unique function names
|
2024-08-21 12:16:44 +02:00
|
|
|
V3UniqueNames m_modeUniqueNames{"__Vmode"}; // For generating unique rand/constraint
|
|
|
|
|
// mode state var names
|
2025-07-25 12:13:46 +02:00
|
|
|
V3UniqueNames m_inlineUniqueStdName{"__VStdrand"};
|
2023-09-16 05:02:34 +02:00
|
|
|
VMemberMap m_memberMap; // Member names cached for fast lookup
|
2022-11-12 03:53:05 +01:00
|
|
|
AstNodeModule* m_modp = nullptr; // Current module
|
2025-07-25 12:13:46 +02:00
|
|
|
std::unordered_map<AstNodeModule*, AstVar*> m_stdMap; // Map from module/class to AST Var
|
2022-11-19 16:48:46 +01:00
|
|
|
const AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNodeStmt* m_stmtp = nullptr; // Current statement
|
|
|
|
|
AstDynArrayDType* m_dynarrayDtp = nullptr; // Dynamic array type (for rand mode)
|
2020-12-07 23:55:22 +01:00
|
|
|
size_t m_enumValueTabCount = 0; // Number of tables with enum values created
|
2022-11-12 03:53:05 +01:00
|
|
|
int m_randCaseNum = 0; // Randcase number within a module for var naming
|
2023-09-19 03:17:21 +02:00
|
|
|
std::map<std::string, AstCDType*> m_randcDtypes; // RandC data type deduplication
|
2024-11-15 16:45:06 +01:00
|
|
|
AstConstraint* m_constraintp = nullptr; // Current constraint
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
// METHODS
|
2024-07-19 19:03:48 +02:00
|
|
|
void createRandomGenerator(AstClass* const classp) {
|
|
|
|
|
if (classp->user3p()) return;
|
|
|
|
|
if (classp->extendsp()) {
|
|
|
|
|
createRandomGenerator(classp->extendsp()->classp());
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-07-18 14:57:39 +02:00
|
|
|
AstVar* const genp = new AstVar{classp->fileline(), VVarType::MEMBER, "constraint",
|
|
|
|
|
classp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR)};
|
|
|
|
|
genp->user2p(classp);
|
|
|
|
|
classp->addMembersp(genp);
|
|
|
|
|
classp->user3p(genp);
|
2024-07-19 19:03:48 +02:00
|
|
|
}
|
2025-07-25 12:13:46 +02:00
|
|
|
AstVar* createStdRandomGenerator(AstNodeModule* const modp) {
|
|
|
|
|
auto it = m_stdMap.find(modp);
|
|
|
|
|
if (it == m_stdMap.end()) {
|
|
|
|
|
AstVar* const stdgenp
|
|
|
|
|
= new AstVar{modp->fileline(), VVarType::MEMBER, "stdrand",
|
|
|
|
|
modp->findBasicDType(VBasicDTypeKwd::RANDOM_STDGENERATOR)};
|
2025-08-28 03:25:40 +02:00
|
|
|
stdgenp->fileline()->warnOff(V3ErrorCode::IMPURE, true);
|
2025-07-25 12:13:46 +02:00
|
|
|
modp->addStmtsp(stdgenp);
|
|
|
|
|
m_stdMap.emplace(modp, stdgenp);
|
|
|
|
|
return stdgenp;
|
|
|
|
|
}
|
|
|
|
|
return it->second;
|
|
|
|
|
}
|
2024-07-19 19:03:48 +02:00
|
|
|
AstVar* getRandomGenerator(AstClass* const classp) {
|
|
|
|
|
if (classp->user3p()) return VN_AS(classp->user3p(), Var);
|
|
|
|
|
if (classp->extendsp()) return getRandomGenerator(classp->extendsp()->classp());
|
|
|
|
|
return nullptr;
|
2024-07-18 14:57:39 +02:00
|
|
|
}
|
|
|
|
|
AstTask* getCreateConstraintSetupFunc(AstClass* classp) {
|
2024-08-21 12:16:44 +02:00
|
|
|
static const char* const name = "__Vsetup_constraints";
|
|
|
|
|
AstTask* setupAllTaskp = VN_AS(m_memberMap.findMember(classp, name), Task);
|
|
|
|
|
if (setupAllTaskp) return setupAllTaskp;
|
|
|
|
|
setupAllTaskp = new AstTask{classp->fileline(), "__Vsetup_constraints", nullptr};
|
2024-07-18 14:57:39 +02:00
|
|
|
setupAllTaskp->classMethod(true);
|
|
|
|
|
setupAllTaskp->isVirtual(true);
|
|
|
|
|
classp->addMembersp(setupAllTaskp);
|
2024-08-21 12:16:44 +02:00
|
|
|
m_memberMap.insert(classp, setupAllTaskp);
|
2024-07-18 14:57:39 +02:00
|
|
|
return setupAllTaskp;
|
|
|
|
|
}
|
2024-11-15 16:45:06 +01:00
|
|
|
AstTask* getCreateAggrResizeTask(AstClass* const classp) {
|
|
|
|
|
static const char* const name = "__Vresize_constrained_arrays";
|
|
|
|
|
AstTask* resizeTaskp = VN_AS(m_memberMap.findMember(classp, name), Task);
|
|
|
|
|
if (resizeTaskp) return resizeTaskp;
|
|
|
|
|
resizeTaskp = new AstTask{classp->fileline(), name, nullptr};
|
|
|
|
|
resizeTaskp->classMethod(true);
|
|
|
|
|
resizeTaskp->isVirtual(true);
|
|
|
|
|
classp->addMembersp(resizeTaskp);
|
|
|
|
|
m_memberMap.insert(classp, resizeTaskp);
|
|
|
|
|
return resizeTaskp;
|
|
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
AstVar* getCreateRandModeVar(AstClass* const classp) {
|
2024-08-21 12:16:44 +02:00
|
|
|
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
2024-07-31 23:30:48 +02:00
|
|
|
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
|
|
|
|
return getCreateRandModeVar(extendsp->classp());
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
AstVar* const randModeVarp = createModeVar(classp, "__Vrandmode");
|
|
|
|
|
classp->user2p(randModeVarp);
|
|
|
|
|
return randModeVarp;
|
|
|
|
|
}
|
|
|
|
|
static AstVar* getRandModeVar(AstClass* const classp) {
|
|
|
|
|
if (classp->user2p()) return VN_AS(classp->user2p(), Var);
|
|
|
|
|
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
|
|
|
|
return getRandModeVar(extendsp->classp());
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
AstVar* getCreateConstraintModeVar(AstClass* const classp) {
|
|
|
|
|
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
|
|
|
|
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
|
|
|
|
return getCreateConstraintModeVar(extendsp->classp());
|
|
|
|
|
}
|
|
|
|
|
AstVar* const constraintModeVarp = createModeVar(classp, "__Vconstraintmode");
|
|
|
|
|
classp->user4p(constraintModeVarp);
|
|
|
|
|
return constraintModeVarp;
|
|
|
|
|
}
|
|
|
|
|
static AstVar* getConstraintModeVar(AstClass* const classp) {
|
|
|
|
|
if (classp->user4p()) return VN_AS(classp->user4p(), Var);
|
|
|
|
|
if (AstClassExtends* const extendsp = classp->extendsp()) {
|
|
|
|
|
return getConstraintModeVar(extendsp->classp());
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
AstVar* createModeVar(AstClass* const classp, const char* const name) {
|
2024-07-31 23:30:48 +02:00
|
|
|
FileLine* const fl = classp->fileline();
|
|
|
|
|
if (!m_dynarrayDtp) {
|
|
|
|
|
m_dynarrayDtp = new AstDynArrayDType{
|
|
|
|
|
fl, v3Global.rootp()->typeTablep()->findBitDType()->dtypep()};
|
|
|
|
|
m_dynarrayDtp->dtypep(m_dynarrayDtp);
|
|
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(m_dynarrayDtp);
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
AstVar* const modeVarp = new AstVar{fl, VVarType::MODULETEMP, name, m_dynarrayDtp};
|
|
|
|
|
modeVarp->user2p(classp);
|
|
|
|
|
classp->addStmtsp(modeVarp);
|
|
|
|
|
return modeVarp;
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
static void addSetRandMode(AstNodeFTask* const ftaskp, AstVar* const genp,
|
|
|
|
|
AstVar* const randModeVarp) {
|
2024-07-31 23:30:48 +02:00
|
|
|
FileLine* const fl = ftaskp->fileline();
|
|
|
|
|
AstCMethodHard* const setRandModep = new AstCMethodHard{
|
|
|
|
|
fl, new AstVarRef{fl, VN_AS(genp->user2p(), NodeModule), genp, VAccess::WRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::CLASS_SET_RANDMODE,
|
2024-07-31 23:30:48 +02:00
|
|
|
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), randModeVarp,
|
|
|
|
|
VAccess::READ}};
|
|
|
|
|
setRandModep->dtypeSetVoid();
|
|
|
|
|
ftaskp->addStmtsp(setRandModep->makeStmt());
|
|
|
|
|
}
|
2024-07-19 19:03:48 +02:00
|
|
|
void createRandomizeClassVars(AstNetlist* const netlistp) {
|
2024-08-21 12:16:44 +02:00
|
|
|
netlistp->foreach([this](AstClass* const classp) {
|
|
|
|
|
bool hasConstraints = false;
|
2024-07-31 23:30:48 +02:00
|
|
|
uint32_t randModeCount = 0;
|
2024-08-21 12:16:44 +02:00
|
|
|
uint32_t constraintModeCount = 0;
|
|
|
|
|
classp->foreachMember([&](AstClass*, AstNode* memberp) {
|
2024-07-31 23:30:48 +02:00
|
|
|
// SystemVerilog only allows single inheritance, so we don't need to worry about
|
|
|
|
|
// index overlap. If the index > 0, it's already been set.
|
2024-08-21 12:16:44 +02:00
|
|
|
if (VN_IS(memberp, Constraint)) {
|
|
|
|
|
hasConstraints = true;
|
|
|
|
|
RandomizeMode constraintMode = {.asInt = memberp->user1()};
|
|
|
|
|
if (!constraintMode.usesMode) return;
|
|
|
|
|
if (constraintMode.index == 0) {
|
|
|
|
|
constraintMode.index = constraintModeCount++;
|
|
|
|
|
memberp->user1(constraintMode.asInt);
|
|
|
|
|
} else {
|
|
|
|
|
constraintModeCount = constraintMode.index + 1;
|
|
|
|
|
}
|
|
|
|
|
} else if (VN_IS(memberp, Var)) {
|
|
|
|
|
RandomizeMode randMode = {.asInt = memberp->user1()};
|
|
|
|
|
if (!randMode.usesMode) return;
|
|
|
|
|
if (randMode.index == 0) {
|
|
|
|
|
randMode.index = randModeCount++;
|
|
|
|
|
memberp->user1(randMode.asInt);
|
|
|
|
|
} else {
|
|
|
|
|
randModeCount = randMode.index + 1;
|
|
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
|
|
|
|
});
|
2024-08-21 12:16:44 +02:00
|
|
|
if (hasConstraints) createRandomGenerator(classp);
|
2024-07-31 23:30:48 +02:00
|
|
|
if (randModeCount > 0) {
|
|
|
|
|
AstVar* const randModeVarp = getCreateRandModeVar(classp);
|
2024-08-21 12:16:44 +02:00
|
|
|
makeModeInit(randModeVarp, classp, randModeCount);
|
|
|
|
|
}
|
|
|
|
|
if (constraintModeCount > 0) {
|
|
|
|
|
AstVar* const constraintModeVarp = getCreateConstraintModeVar(classp);
|
|
|
|
|
makeModeInit(constraintModeVarp, classp, constraintModeCount);
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-07-19 19:03:48 +02:00
|
|
|
});
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
void makeModeInit(AstVar* modeVarp, AstClass* classp, uint32_t modeCount) {
|
|
|
|
|
AstNodeModule* const modeVarModp = VN_AS(modeVarp->user2p(), NodeModule);
|
|
|
|
|
FileLine* fl = modeVarp->fileline();
|
|
|
|
|
AstCMethodHard* const dynarrayNewp
|
|
|
|
|
= new AstCMethodHard{fl, new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::DYN_RESIZE, new AstConst{fl, modeCount}};
|
2024-08-21 12:16:44 +02:00
|
|
|
dynarrayNewp->dtypeSetVoid();
|
|
|
|
|
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(classp, "new"), NodeFTask);
|
|
|
|
|
UASSERT_OBJ(newp, classp, "No new() in class");
|
|
|
|
|
newp->addStmtsp(dynarrayNewp->makeStmt());
|
|
|
|
|
newp->addStmtsp(makeModeSetLoop(fl,
|
|
|
|
|
new AstVarRef{fl, modeVarModp, modeVarp, VAccess::WRITE},
|
|
|
|
|
new AstConst{fl, 1}, true));
|
|
|
|
|
}
|
|
|
|
|
static AstNode* makeModeSetLoop(FileLine* const fl, AstNodeExpr* const lhsp,
|
|
|
|
|
AstNodeExpr* const rhsp, bool inTask) {
|
2024-07-31 23:30:48 +02:00
|
|
|
AstVar* const iterVarp = new AstVar{fl, VVarType::BLOCKTEMP, "i", lhsp->findUInt32DType()};
|
|
|
|
|
iterVarp->funcLocal(inTask);
|
2025-09-22 01:52:19 +02:00
|
|
|
iterVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2025-09-27 14:22:17 +02:00
|
|
|
AstCMethodHard* const sizep = new AstCMethodHard{fl, lhsp, VCMethod::DYN_SIZE, nullptr};
|
2024-07-31 23:30:48 +02:00
|
|
|
sizep->dtypeSetUInt32();
|
2025-09-27 14:22:17 +02:00
|
|
|
AstCMethodHard* const setp
|
|
|
|
|
= new AstCMethodHard{fl, lhsp->cloneTree(false), VCMethod::ARRAY_AT_WRITE,
|
|
|
|
|
new AstVarRef{fl, iterVarp, VAccess::READ}};
|
2024-09-02 15:45:47 +02:00
|
|
|
setp->dtypeSetUInt32();
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNode* const stmtsp = iterVarp;
|
|
|
|
|
stmtsp->addNext(
|
|
|
|
|
new AstAssign{fl, new AstVarRef{fl, iterVarp, VAccess::WRITE}, new AstConst{fl, 0}});
|
2025-09-29 16:25:25 +02:00
|
|
|
|
|
|
|
|
AstLoop* const loopp = new AstLoop{fl};
|
|
|
|
|
stmtsp->addNext(loopp);
|
|
|
|
|
loopp->addStmtsp(new AstLoopTest{
|
|
|
|
|
fl, loopp, new AstLt{fl, new AstVarRef{fl, iterVarp, VAccess::READ}, sizep}});
|
|
|
|
|
loopp->addStmtsp(new AstAssign{fl, setp, rhsp});
|
|
|
|
|
loopp->addStmtsp(new AstAssign{
|
|
|
|
|
fl, new AstVarRef{fl, iterVarp, VAccess::WRITE},
|
|
|
|
|
new AstAdd{fl, new AstConst{fl, 1}, new AstVarRef{fl, iterVarp, VAccess::READ}}});
|
2025-09-23 20:49:01 +02:00
|
|
|
return new AstBegin{fl, "", stmtsp, true};
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
static AstNodeStmt* wrapIfRandMode(AstClass* classp, AstVar* const varp, AstNodeStmt* stmtp) {
|
2024-08-29 16:39:54 +02:00
|
|
|
const RandomizeMode rmode = {.asInt = varp->user1()};
|
|
|
|
|
return VN_AS(wrapIfMode(rmode, getRandModeVar(classp), stmtp), NodeStmt);
|
2024-08-21 12:16:44 +02:00
|
|
|
}
|
|
|
|
|
static AstNode* wrapIfConstraintMode(AstClass* classp, AstConstraint* const constrp,
|
|
|
|
|
AstNode* stmtp) {
|
2024-08-29 16:39:54 +02:00
|
|
|
const RandomizeMode rmode = {.asInt = constrp->user1()};
|
|
|
|
|
return wrapIfMode(rmode, getConstraintModeVar(classp), stmtp);
|
2024-08-21 12:16:44 +02:00
|
|
|
}
|
|
|
|
|
static AstNode* wrapIfMode(const RandomizeMode mode, AstVar* modeVarp, AstNode* stmtp) {
|
2024-07-31 23:30:48 +02:00
|
|
|
FileLine* const fl = stmtp->fileline();
|
2024-08-21 12:16:44 +02:00
|
|
|
if (mode.usesMode) {
|
|
|
|
|
AstCMethodHard* const atp = new AstCMethodHard{
|
|
|
|
|
fl, new AstVarRef{fl, VN_AS(modeVarp->user2p(), Class), modeVarp, VAccess::READ},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::ARRAY_AT, new AstConst{fl, mode.index}};
|
2024-07-31 23:30:48 +02:00
|
|
|
atp->dtypeSetUInt32();
|
|
|
|
|
return new AstIf{fl, atp, stmtp};
|
|
|
|
|
}
|
|
|
|
|
return stmtp;
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVar* enumValueTabp(AstEnumDType* const nodep) {
|
2021-10-22 14:56:48 +02:00
|
|
|
if (nodep->user2p()) return VN_AS(nodep->user2p(), Var);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Construct Venumvaltab " << nodep);
|
2024-06-08 04:52:43 +02:00
|
|
|
AstNodeArrayDType* const vardtypep = new AstUnpackArrayDType{
|
|
|
|
|
nodep->fileline(), nodep->dtypep(),
|
|
|
|
|
new AstRange{nodep->fileline(), static_cast<int>(nodep->itemCount()), 0}};
|
2022-11-13 17:23:57 +01:00
|
|
|
AstInitArray* const initp = new AstInitArray{nodep->fileline(), vardtypep, nullptr};
|
2020-12-07 23:55:22 +01:00
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(vardtypep);
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVar* const varp
|
2022-11-13 17:23:57 +01:00
|
|
|
= new AstVar{nodep->fileline(), VVarType::MODULETEMP,
|
|
|
|
|
"__Venumvaltab_" + cvtToStr(m_enumValueTabCount++), vardtypep};
|
2020-12-07 23:55:22 +01:00
|
|
|
varp->isConst(true);
|
|
|
|
|
varp->isStatic(true);
|
|
|
|
|
varp->valuep(initp);
|
|
|
|
|
// Add to root, as don't know module we are in, and aids later structure sharing
|
2022-09-15 20:43:56 +02:00
|
|
|
v3Global.rootp()->dollarUnitPkgAddp()->addStmtsp(varp);
|
2024-07-12 16:18:18 +02:00
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
UASSERT_OBJ(nodep->itemsp(), nodep, "Enum without items");
|
|
|
|
|
for (AstEnumItem* itemp = nodep->itemsp(); itemp;
|
2021-10-22 14:56:48 +02:00
|
|
|
itemp = VN_AS(itemp->nextp(), EnumItem)) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstConst* const vconstp = VN_AS(itemp->valuep(), Const);
|
2020-12-07 23:55:22 +01:00
|
|
|
UASSERT_OBJ(vconstp, nodep, "Enum item without constified value");
|
|
|
|
|
initp->addValuep(vconstp->cloneTree(false));
|
|
|
|
|
}
|
|
|
|
|
nodep->user2p(varp);
|
|
|
|
|
return varp;
|
|
|
|
|
}
|
2023-09-19 03:17:21 +02:00
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstCDType* findVlRandCDType(FileLine* const fl, uint64_t items) {
|
2023-09-19 03:17:21 +02:00
|
|
|
// For 8 items we need to have a 9 item LFSR so items is max count
|
2024-03-03 16:23:04 +01:00
|
|
|
// width(items) = log2(items) + 1
|
|
|
|
|
const std::string type = AstCDType::typeToHold(V3Number::log2bQuad(items) + 1);
|
2023-09-19 03:17:21 +02:00
|
|
|
const std::string name = "VlRandC<" + type + ", " + cvtToStr(items) + "ULL>";
|
|
|
|
|
// Create or reuse (to avoid duplicates) randomization object dtype
|
2023-10-28 14:38:02 +02:00
|
|
|
const auto pair = m_randcDtypes.emplace(name, nullptr);
|
|
|
|
|
if (pair.second) {
|
|
|
|
|
AstCDType* newp = new AstCDType{fl, name};
|
|
|
|
|
v3Global.rootp()->typeTablep()->addTypesp(newp);
|
|
|
|
|
pair.first->second = newp;
|
|
|
|
|
}
|
|
|
|
|
return pair.first->second;
|
2023-09-19 03:17:21 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVar* newRandcVarsp(AstVar* const varp) {
|
2023-09-19 03:17:21 +02:00
|
|
|
// If a randc, make a VlRandC object to hold the state
|
|
|
|
|
if (!varp->isRandC()) return nullptr;
|
|
|
|
|
uint64_t items = 0;
|
|
|
|
|
|
|
|
|
|
if (AstEnumDType* const enumDtp = VN_CAST(varp->dtypep()->skipRefToEnump(), EnumDType)) {
|
|
|
|
|
items = static_cast<uint64_t>(enumDtp->itemCount());
|
2025-09-06 14:24:19 +02:00
|
|
|
} else if (AstBasicDType* const basicp = varp->dtypep()->skipRefp()->basicp()) {
|
2023-09-19 03:17:21 +02:00
|
|
|
if (basicp->width() > 32) {
|
2023-11-11 05:25:53 +01:00
|
|
|
varp->v3error("Maximum implemented width for randc is 32 bits, "
|
2023-09-19 03:17:21 +02:00
|
|
|
<< varp->prettyNameQ() << " is " << basicp->width() << " bits");
|
2024-08-13 20:20:31 +02:00
|
|
|
varp->rand(VRandAttr::RAND);
|
2023-09-19 03:17:21 +02:00
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
items = 1ULL << basicp->width();
|
2025-09-06 14:24:19 +02:00
|
|
|
} else if (AstStructDType* const dtp = VN_CAST(varp->dtypep()->skipRefp(), StructDType)) {
|
|
|
|
|
UASSERT_OBJ(!dtp->packed(), dtp, "skipRef should have hidden packed before got here");
|
|
|
|
|
dtp->v3error("Unpacked structs shall not be declared as randc"
|
|
|
|
|
" (IEEE 1800-2023 18.4)");
|
|
|
|
|
return nullptr;
|
|
|
|
|
} else {
|
|
|
|
|
varp->v3fatalSrc("Unexpected randc variable dtype");
|
2023-09-19 03:17:21 +02:00
|
|
|
}
|
|
|
|
|
AstCDType* newdtp = findVlRandCDType(varp->fileline(), items);
|
|
|
|
|
AstVar* newp
|
|
|
|
|
= new AstVar{varp->fileline(), VVarType::MEMBER, varp->name() + "__Vrandc", newdtp};
|
|
|
|
|
newp->isInternal(true);
|
|
|
|
|
varp->addNextHere(newp);
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "created " << varp);
|
2023-09-19 03:17:21 +02:00
|
|
|
return newp;
|
|
|
|
|
}
|
2024-09-20 02:07:05 +02:00
|
|
|
AstNodeStmt* createArrayForeachLoop(FileLine* const fl, AstNodeDType* const dtypep,
|
2024-10-25 18:00:43 +02:00
|
|
|
AstNodeExpr* exprp, AstVar* const outputVarp) {
|
2025-09-09 23:39:44 +02:00
|
|
|
V3UniqueNames uniqueNames{"__Vrandarr"};
|
2024-09-20 02:07:05 +02:00
|
|
|
AstNodeDType* tempDTypep = dtypep;
|
|
|
|
|
AstVar* randLoopIndxp = nullptr;
|
|
|
|
|
auto createLoopIndex = [&](AstNodeDType* tempDTypep) {
|
2024-10-08 16:54:20 +02:00
|
|
|
if (VN_IS(tempDTypep, AssocArrayDType)) {
|
2025-09-09 23:39:44 +02:00
|
|
|
return new AstVar{fl, VVarType::VAR, uniqueNames.get(""),
|
2025-01-09 14:33:38 +01:00
|
|
|
VN_AS(tempDTypep, AssocArrayDType)->keyDTypep()};
|
2024-10-08 16:54:20 +02:00
|
|
|
}
|
2025-09-09 23:39:44 +02:00
|
|
|
return new AstVar{fl, VVarType::VAR, uniqueNames.get(""),
|
2024-09-20 02:07:05 +02:00
|
|
|
dtypep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
|
|
|
|
};
|
|
|
|
|
AstNodeExpr* tempElementp = nullptr;
|
2024-10-08 16:54:20 +02:00
|
|
|
while (VN_IS(tempDTypep, DynArrayDType) || VN_IS(tempDTypep, UnpackArrayDType)
|
|
|
|
|
|| VN_IS(tempDTypep, AssocArrayDType) || VN_IS(tempDTypep, QueueDType)) {
|
2024-09-20 02:07:05 +02:00
|
|
|
AstVar* const newRandLoopIndxp = createLoopIndex(tempDTypep);
|
|
|
|
|
randLoopIndxp = AstNode::addNext(randLoopIndxp, newRandLoopIndxp);
|
2025-09-16 20:22:36 +02:00
|
|
|
AstNodeExpr* const tempExprp = tempElementp ? tempElementp : exprp;
|
|
|
|
|
AstVarRef* const tempRefp = new AstVarRef{fl, newRandLoopIndxp, VAccess::READ};
|
|
|
|
|
if (VN_IS(tempDTypep, DynArrayDType)) {
|
2025-09-27 14:22:17 +02:00
|
|
|
tempElementp
|
|
|
|
|
= new AstCMethodHard{fl, tempExprp, VCMethod::ARRAY_AT_WRITE, tempRefp};
|
2025-09-16 20:22:36 +02:00
|
|
|
} else if (VN_IS(tempDTypep, UnpackArrayDType)) {
|
2024-10-21 15:56:50 +02:00
|
|
|
AstNodeArrayDType* const aryDTypep = VN_CAST(tempDTypep, NodeArrayDType);
|
|
|
|
|
// Adjust the bitp to ensure it covers all possible indices
|
|
|
|
|
tempElementp = new AstArraySel{
|
|
|
|
|
fl, tempExprp,
|
|
|
|
|
new AstSel{
|
|
|
|
|
fl,
|
|
|
|
|
new AstSub{fl, tempRefp,
|
|
|
|
|
new AstConst{fl, static_cast<uint32_t>(aryDTypep->lo())}},
|
2025-06-24 17:59:09 +02:00
|
|
|
new AstConst{fl, 0}, V3Number::log2b(aryDTypep->hi()) + 1}};
|
2025-09-16 20:22:36 +02:00
|
|
|
} else if (VN_IS(tempDTypep, AssocArrayDType)) {
|
2024-10-08 16:54:20 +02:00
|
|
|
tempElementp = new AstAssocSel{fl, tempExprp, tempRefp};
|
2025-09-16 20:22:36 +02:00
|
|
|
} else if (VN_IS(tempDTypep, QueueDType)) {
|
2025-09-27 14:22:17 +02:00
|
|
|
tempElementp
|
|
|
|
|
= new AstCMethodHard{fl, tempExprp, VCMethod::DYN_AT_WRITE_APPEND, tempRefp};
|
2025-09-16 20:22:36 +02:00
|
|
|
}
|
2024-09-20 02:07:05 +02:00
|
|
|
tempElementp->dtypep(tempDTypep->subDTypep());
|
|
|
|
|
tempDTypep = tempDTypep->virtRefDTypep();
|
|
|
|
|
}
|
2025-09-16 20:22:36 +02:00
|
|
|
|
|
|
|
|
AstSelLoopVars* const randLoopVarp
|
|
|
|
|
= new AstSelLoopVars{fl, exprp->cloneTree(false), randLoopIndxp};
|
|
|
|
|
AstNodeStmt* const randStmtsp = newRandStmtsp(fl, tempElementp, nullptr, outputVarp);
|
|
|
|
|
// TODO: we should just not clone in 'newRandStmtsp' if not necessary
|
|
|
|
|
if (!tempElementp->backp()) VL_DO_DANGLING(pushDeletep(tempElementp), tempElementp);
|
|
|
|
|
return new AstForeach{fl, randLoopVarp, randStmtsp};
|
2024-09-20 02:07:05 +02:00
|
|
|
}
|
2024-10-25 18:00:43 +02:00
|
|
|
AstNodeStmt* newRandStmtsp(FileLine* fl, AstNodeExpr* exprp, AstVar* randcVarp,
|
|
|
|
|
AstVar* const outputVarp, int offset = 0,
|
2024-07-11 16:43:56 +02:00
|
|
|
AstMemberDType* memberp = nullptr) {
|
2024-09-20 02:07:05 +02:00
|
|
|
AstNodeDType* const memberDtp
|
|
|
|
|
= memberp ? memberp->subDTypep()->skipRefp() : exprp->dtypep()->skipRefp();
|
|
|
|
|
if (const auto* const structDtp = VN_CAST(memberDtp, StructDType)) {
|
2020-12-07 23:55:22 +01:00
|
|
|
AstNodeStmt* stmtsp = nullptr;
|
2024-07-11 16:43:56 +02:00
|
|
|
if (structDtp->packed()) offset += memberp ? memberp->lsb() : 0;
|
2022-11-13 17:23:57 +01:00
|
|
|
for (AstMemberDType* smemberp = structDtp->membersp(); smemberp;
|
2021-10-22 14:56:48 +02:00
|
|
|
smemberp = VN_AS(smemberp->nextp(), MemberDType)) {
|
2024-07-11 16:43:56 +02:00
|
|
|
AstNodeStmt* randp = nullptr;
|
|
|
|
|
if (structDtp->packed()) {
|
|
|
|
|
randp = newRandStmtsp(fl, stmtsp ? exprp->cloneTree(false) : exprp, nullptr,
|
2024-10-25 18:00:43 +02:00
|
|
|
outputVarp, offset, smemberp);
|
2024-07-11 16:43:56 +02:00
|
|
|
} else {
|
|
|
|
|
AstStructSel* structSelp
|
|
|
|
|
= new AstStructSel{fl, exprp->cloneTree(false), smemberp->name()};
|
|
|
|
|
structSelp->dtypep(smemberp->childDTypep());
|
|
|
|
|
if (!structSelp->dtypep()) structSelp->dtypep(smemberp->subDTypep());
|
2024-10-25 18:00:43 +02:00
|
|
|
randp = newRandStmtsp(fl, structSelp, nullptr, outputVarp);
|
2024-07-11 16:43:56 +02:00
|
|
|
}
|
2024-09-20 02:07:05 +02:00
|
|
|
stmtsp = stmtsp ? stmtsp->addNext(randp) : randp;
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
return stmtsp;
|
2024-09-20 02:07:05 +02:00
|
|
|
} else if (const auto* const unionDtp = VN_CAST(memberDtp, UnionDType)) {
|
2024-08-26 17:04:45 +02:00
|
|
|
if (!unionDtp->packed()) {
|
|
|
|
|
unionDtp->v3error("Unpacked unions shall not be declared as rand or randc."
|
|
|
|
|
" (IEEE 1800-2023 18.4)");
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
AstMemberDType* const firstMemberp = unionDtp->membersp();
|
2024-10-25 18:00:43 +02:00
|
|
|
return newRandStmtsp(fl, exprp, nullptr, outputVarp, offset, firstMemberp);
|
|
|
|
|
} else if (const AstClassRefDType* const classRefDtp = VN_CAST(memberDtp, ClassRefDType)) {
|
|
|
|
|
AstFunc* const memberFuncp
|
|
|
|
|
= V3Randomize::newRandomizeFunc(m_memberMap, classRefDtp->classp());
|
|
|
|
|
AstMethodCall* const callp = new AstMethodCall{fl, exprp, "randomize", nullptr};
|
|
|
|
|
callp->taskp(memberFuncp);
|
|
|
|
|
callp->dtypeFrom(memberFuncp);
|
2024-11-08 12:53:43 +01:00
|
|
|
AstAssign* const assignp = new AstAssign{
|
2024-10-25 18:00:43 +02:00
|
|
|
fl, new AstVarRef{fl, outputVarp, VAccess::WRITE},
|
|
|
|
|
new AstAnd{fl, new AstVarRef{fl, outputVarp, VAccess::READ}, callp}};
|
2024-11-08 12:53:43 +01:00
|
|
|
return new AstIf{
|
|
|
|
|
fl, new AstNeq{fl, exprp->cloneTree(false), new AstConst{fl, AstConst::Null{}}},
|
|
|
|
|
assignp};
|
2024-09-20 02:07:05 +02:00
|
|
|
} else if (AstDynArrayDType* const dynarrayDtp = VN_CAST(memberDtp, DynArrayDType)) {
|
2024-10-25 18:00:43 +02:00
|
|
|
return createArrayForeachLoop(fl, dynarrayDtp, exprp, outputVarp);
|
2024-10-08 16:54:20 +02:00
|
|
|
} else if (AstQueueDType* const queueDtp = VN_CAST(memberDtp, QueueDType)) {
|
2024-10-25 18:00:43 +02:00
|
|
|
return createArrayForeachLoop(fl, queueDtp, exprp, outputVarp);
|
2024-09-20 02:07:05 +02:00
|
|
|
} else if (AstUnpackArrayDType* const unpackarrayDtp
|
|
|
|
|
= VN_CAST(memberDtp, UnpackArrayDType)) {
|
2024-10-25 18:00:43 +02:00
|
|
|
return createArrayForeachLoop(fl, unpackarrayDtp, exprp, outputVarp);
|
2024-10-08 16:54:20 +02:00
|
|
|
} else if (AstAssocArrayDType* const assocarrayDtp = VN_CAST(memberDtp, AssocArrayDType)) {
|
2024-10-25 18:00:43 +02:00
|
|
|
return createArrayForeachLoop(fl, assocarrayDtp, exprp, outputVarp);
|
2020-12-07 23:55:22 +01:00
|
|
|
} else {
|
2022-10-12 11:19:21 +02:00
|
|
|
AstNodeExpr* valp;
|
2022-11-13 17:23:57 +01:00
|
|
|
if (AstEnumDType* const enumDtp = VN_CAST(memberp ? memberp->subDTypep()->subDTypep()
|
2024-07-11 16:43:56 +02:00
|
|
|
: exprp->dtypep()->subDTypep(),
|
2022-11-13 17:23:57 +01:00
|
|
|
EnumDType)) {
|
2021-11-13 19:50:44 +01:00
|
|
|
AstVarRef* const tabRefp
|
2022-11-13 17:23:57 +01:00
|
|
|
= new AstVarRef{fl, enumValueTabp(enumDtp), VAccess::READ};
|
2020-12-07 23:55:22 +01:00
|
|
|
tabRefp->classOrPackagep(v3Global.rootp()->dollarUnitPkgAddp());
|
2023-09-19 03:17:21 +02:00
|
|
|
AstNodeExpr* const randp
|
2024-07-11 16:43:56 +02:00
|
|
|
= newRandValue(fl, randcVarp, exprp->findBasicDType(VBasicDTypeKwd::UINT32));
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const moddivp = new AstModDiv{
|
2022-11-13 17:23:57 +01:00
|
|
|
fl, randp, new AstConst{fl, static_cast<uint32_t>(enumDtp->itemCount())}};
|
2020-12-07 23:55:22 +01:00
|
|
|
moddivp->dtypep(enumDtp);
|
2022-11-13 17:23:57 +01:00
|
|
|
valp = new AstArraySel{fl, tabRefp, moddivp};
|
2020-12-07 23:55:22 +01:00
|
|
|
} else {
|
2024-07-11 16:43:56 +02:00
|
|
|
valp
|
|
|
|
|
= newRandValue(fl, randcVarp, (memberp ? memberp->dtypep() : exprp->dtypep()));
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
AstAssign* assignp
|
|
|
|
|
= new AstAssign{fl,
|
|
|
|
|
new AstSel{fl, exprp, offset + (memberp ? memberp->lsb() : 0),
|
|
|
|
|
memberp ? memberp->width() : exprp->width()},
|
|
|
|
|
valp};
|
|
|
|
|
AstVar* varp = nullptr;
|
|
|
|
|
exprp->exists([&](const AstVarRef* varrefp) {
|
|
|
|
|
if (varrefp->access().isWriteOrRW()) varp = varrefp->varp();
|
|
|
|
|
return varp != nullptr;
|
|
|
|
|
});
|
2024-08-21 12:16:44 +02:00
|
|
|
return wrapIfRandMode(VN_AS(m_modp, Class), varp, assignp);
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
AstNodeExpr* newRandValue(FileLine* const fl, AstVar* const randcVarp,
|
|
|
|
|
AstNodeDType* const dtypep) {
|
2023-09-19 03:17:21 +02:00
|
|
|
if (randcVarp) {
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCExpr* const cexprp = new AstCExpr{fl};
|
|
|
|
|
cexprp->add(new AstVarRef{fl, randcVarp, VAccess::READWRITE});
|
|
|
|
|
cexprp->add(".randomize(__Vm_rng)");
|
|
|
|
|
cexprp->dtypep(dtypep);
|
|
|
|
|
return cexprp;
|
2023-09-19 03:17:21 +02:00
|
|
|
}
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
|
|
|
|
|
return new AstRandRNG{fl, dtypep};
|
2023-09-19 03:17:21 +02:00
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
void addPrePostCall(AstClass* const classp, AstFunc* const funcp, const string& name) {
|
2023-09-16 05:02:34 +02:00
|
|
|
if (AstTask* userFuncp = VN_CAST(m_memberMap.findMember(classp, name), Task)) {
|
2024-12-15 15:15:49 +01:00
|
|
|
AstTaskRef* const callp = new AstTaskRef{userFuncp->fileline(), userFuncp, nullptr};
|
2022-11-13 17:59:40 +01:00
|
|
|
funcp->addStmtsp(callp->makeStmt());
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
AstTask* newSetupConstraintTask(AstClass* const nodep, const std::string& name) {
|
2024-05-17 16:38:34 +02:00
|
|
|
AstTask* const taskp = new AstTask{nodep->fileline(), name + "_setup_constraint", nullptr};
|
|
|
|
|
taskp->classMethod(true);
|
|
|
|
|
nodep->addMembersp(taskp);
|
|
|
|
|
return taskp;
|
|
|
|
|
}
|
2024-11-15 16:45:06 +01:00
|
|
|
AstTask* newResizeConstrainedArrayTask(AstClass* const nodep, const std::string& name) {
|
|
|
|
|
AstTask* const taskp
|
|
|
|
|
= new AstTask{nodep->fileline(), name + "_resize_constrained_array", nullptr};
|
|
|
|
|
taskp->classMethod(true);
|
|
|
|
|
nodep->addMembersp(taskp);
|
|
|
|
|
return taskp;
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
AstNodeStmt* implementConstraintsClear(FileLine* const fileline, AstVar* const genp) {
|
|
|
|
|
AstCMethodHard* const clearp = new AstCMethodHard{
|
2024-07-17 08:21:45 +02:00
|
|
|
fileline,
|
|
|
|
|
new AstVarRef{fileline, VN_AS(genp->user2p(), NodeModule), genp, VAccess::READWRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::RANDOMIZER_CLEAR};
|
2024-07-12 16:18:18 +02:00
|
|
|
clearp->dtypeSetVoid();
|
|
|
|
|
return clearp->makeStmt();
|
|
|
|
|
}
|
2024-08-13 20:20:31 +02:00
|
|
|
AstVar* getVarFromRef(AstNodeExpr* const exprp) {
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
|
|
|
return memberSelp->varp();
|
|
|
|
|
} else if (AstVarRef* const varrefp = VN_CAST(exprp, VarRef)) {
|
|
|
|
|
return varrefp->varp();
|
|
|
|
|
}
|
|
|
|
|
exprp->v3fatalSrc("Not a MemberSel nor VarRef");
|
|
|
|
|
return nullptr; // LCOV_EXCL_LINE
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* makeSiblingRefp(AstNodeExpr* const exprp, AstVar* const varp,
|
|
|
|
|
const VAccess access) {
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
|
|
|
return new AstMemberSel{exprp->fileline(), memberSelp->fromp()->cloneTree(false),
|
|
|
|
|
varp};
|
|
|
|
|
}
|
|
|
|
|
UASSERT_OBJ(VN_IS(exprp, VarRef), exprp, "Should be a VarRef");
|
|
|
|
|
return new AstVarRef{exprp->fileline(), VN_AS(varp->user2p(), Class), varp, access};
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* getFromp(AstNodeExpr* const exprp) {
|
|
|
|
|
if (AstMemberSel* const memberSelp = VN_CAST(exprp, MemberSel)) {
|
|
|
|
|
return memberSelp->fromp();
|
|
|
|
|
} else if (AstMethodCall* const methodCallp = VN_CAST(exprp, MethodCall)) {
|
|
|
|
|
return methodCallp->fromp();
|
|
|
|
|
}
|
|
|
|
|
return nullptr;
|
|
|
|
|
}
|
|
|
|
|
AstVar* makeTmpRandModeVar(AstNodeExpr* siblingExprp, AstVar* randModeVarp,
|
|
|
|
|
AstNode*& storeStmtspr, AstNodeStmt*& restoreStmtspr) {
|
|
|
|
|
FileLine* const fl = randModeVarp->fileline();
|
2024-08-21 12:16:44 +02:00
|
|
|
AstVar* const randModeTmpVarp = new AstVar{
|
|
|
|
|
fl, VVarType::BLOCKTEMP, m_modeUniqueNames.get(randModeVarp), randModeVarp->dtypep()};
|
2024-08-13 20:20:31 +02:00
|
|
|
randModeTmpVarp->funcLocal(m_ftaskp);
|
2025-09-22 01:52:19 +02:00
|
|
|
randModeTmpVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2024-08-13 20:20:31 +02:00
|
|
|
storeStmtspr = AstNode::addNext(
|
|
|
|
|
storeStmtspr,
|
|
|
|
|
new AstAssign{fl, new AstVarRef{fl, randModeTmpVarp, VAccess::WRITE},
|
|
|
|
|
makeSiblingRefp(siblingExprp, randModeVarp, VAccess::READ)});
|
|
|
|
|
storeStmtspr = AstNode::addNext(
|
|
|
|
|
storeStmtspr,
|
2024-08-21 12:16:44 +02:00
|
|
|
makeModeSetLoop(fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
|
|
|
|
|
new AstConst{fl, 0}, m_ftaskp));
|
2024-08-13 20:20:31 +02:00
|
|
|
restoreStmtspr = AstNode::addNext(
|
|
|
|
|
restoreStmtspr,
|
|
|
|
|
new AstAssign{fl, makeSiblingRefp(siblingExprp, randModeVarp, VAccess::WRITE),
|
|
|
|
|
new AstVarRef{fl, randModeTmpVarp, VAccess::READ}});
|
|
|
|
|
return randModeTmpVarp;
|
|
|
|
|
}
|
|
|
|
|
// Returns the common prefix of two hierarchical accesses, or nullptr if there is none
|
|
|
|
|
// e.g. a.b.c and a.b.d -> a.b
|
|
|
|
|
AstNodeExpr* sliceToCommonPrefix(AstNodeExpr* thisp, AstNodeExpr* otherp) {
|
|
|
|
|
static std::vector<AstNodeExpr*> thisHier, otherHier; // Keep around
|
|
|
|
|
// to avoid reallocations
|
|
|
|
|
thisHier.clear();
|
|
|
|
|
otherHier.clear();
|
|
|
|
|
while (thisp) {
|
|
|
|
|
thisHier.push_back(thisp);
|
|
|
|
|
thisp = getFromp(thisp);
|
|
|
|
|
}
|
|
|
|
|
while (otherp) {
|
|
|
|
|
otherHier.push_back(otherp);
|
|
|
|
|
otherp = getFromp(otherp);
|
|
|
|
|
}
|
|
|
|
|
AstNodeExpr* commonp = nullptr;
|
|
|
|
|
for (auto thisIt = thisHier.rbegin(), otherIt = otherHier.rbegin();
|
|
|
|
|
thisIt != thisHier.rend() && otherIt != otherHier.rend(); ++thisIt, ++otherIt) {
|
|
|
|
|
if ((*thisIt)->type() != (*otherIt)->type()) break;
|
|
|
|
|
if (AstMemberSel* memberSelp = VN_CAST(*thisIt, MemberSel)) {
|
|
|
|
|
AstMemberSel* otherMemberSelp = VN_AS(*otherIt, MemberSel);
|
|
|
|
|
if (memberSelp->varp() == otherMemberSelp->varp()) {
|
|
|
|
|
commonp = memberSelp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else if (AstMethodCall* thisMethodCallp = VN_CAST(*thisIt, MethodCall)) {
|
|
|
|
|
AstMethodCall* otherMethodCallp = VN_AS(*otherIt, MethodCall);
|
|
|
|
|
if (thisMethodCallp->taskp() == otherMethodCallp->taskp()) {
|
|
|
|
|
commonp = thisMethodCallp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
} else if (AstVarRef* firstVarRefp = VN_CAST(*thisIt, VarRef)) {
|
|
|
|
|
AstVarRef* secondVarRefp = VN_AS(*otherIt, VarRef);
|
|
|
|
|
if (firstVarRefp->varp() == secondVarRefp->varp()) {
|
|
|
|
|
commonp = firstVarRefp;
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
return commonp;
|
|
|
|
|
}
|
2020-12-07 23:55:22 +01:00
|
|
|
|
2024-08-13 20:20:31 +02:00
|
|
|
void addBasicRandomizeBody(AstFunc* const basicRandomizep, AstClass* const nodep,
|
|
|
|
|
AstVar* randModeVarp) {
|
2024-07-26 11:46:30 +02:00
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
AstVar* const basicFvarp = VN_AS(basicRandomizep->fvarp(), Var);
|
|
|
|
|
AstVarRef* const basicFvarRefp = new AstVarRef{fl, basicFvarp, VAccess::WRITE};
|
|
|
|
|
AstConst* const beginBasicValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
|
|
|
|
|
basicRandomizep->addStmtsp(new AstAssign{fl, basicFvarRefp, beginBasicValp});
|
2024-08-13 20:20:31 +02:00
|
|
|
AstNodeFTask* const newp = VN_AS(m_memberMap.findMember(nodep, "new"), NodeFTask);
|
|
|
|
|
UASSERT_OBJ(newp, nodep, "No new() in class");
|
2024-07-26 11:46:30 +02:00
|
|
|
nodep->foreachMember([&](AstClass* classp, AstVar* memberVarp) {
|
2024-08-13 20:20:31 +02:00
|
|
|
if (!memberVarp->rand().isRandomizable()) return;
|
2024-08-21 12:16:44 +02:00
|
|
|
const RandomizeMode randMode = {.asInt = memberVarp->user1()};
|
|
|
|
|
if (randMode.usesMode
|
|
|
|
|
&& !memberVarp->rand().isRand()) { // Not randomizable by default
|
2024-09-02 15:45:47 +02:00
|
|
|
AstCMethodHard* setp = new AstCMethodHard{
|
2024-08-13 20:20:31 +02:00
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{fl, VN_AS(randModeVarp->user2p(), NodeModule), randModeVarp,
|
|
|
|
|
VAccess::WRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::ARRAY_AT_WRITE, new AstConst{nodep->fileline(), randMode.index}};
|
2024-09-02 15:45:47 +02:00
|
|
|
setp->dtypeSetUInt32();
|
|
|
|
|
newp->addStmtsp(new AstAssign{fl, setp, new AstConst{fl, 0}});
|
2024-08-13 20:20:31 +02:00
|
|
|
}
|
|
|
|
|
if (memberVarp->user3()) return; // Handled in constraints
|
2024-07-26 11:46:30 +02:00
|
|
|
const AstNodeDType* const dtypep = memberVarp->dtypep()->skipRefp();
|
2024-10-08 16:54:20 +02:00
|
|
|
if (const AstClassRefDType* const classRefp = VN_CAST(dtypep, ClassRefDType)) {
|
2024-07-26 11:46:30 +02:00
|
|
|
if (classRefp->classp() == nodep) {
|
|
|
|
|
memberVarp->v3warn(E_UNSUPPORTED,
|
|
|
|
|
"Unsupported: random member variable with the "
|
|
|
|
|
"type of the containing class");
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstFunc* const memberFuncp
|
2025-10-22 22:15:06 +02:00
|
|
|
= memberVarp->globalConstrained()
|
2025-09-19 11:03:56 +02:00
|
|
|
? V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp(),
|
|
|
|
|
BASIC_RANDOMIZE_FUNC_NAME)
|
|
|
|
|
: V3Randomize::newRandomizeFunc(m_memberMap, classRefp->classp());
|
2024-07-26 11:46:30 +02:00
|
|
|
AstMethodCall* const callp
|
2025-10-22 22:15:06 +02:00
|
|
|
= memberVarp->globalConstrained()
|
2025-09-19 11:03:56 +02:00
|
|
|
? new AstMethodCall{fl,
|
|
|
|
|
new AstVarRef{fl, classp, memberVarp,
|
|
|
|
|
VAccess::WRITE},
|
|
|
|
|
BASIC_RANDOMIZE_FUNC_NAME, nullptr}
|
|
|
|
|
: new AstMethodCall{
|
2025-09-19 11:05:12 +02:00
|
|
|
fl, new AstVarRef{fl, classp, memberVarp, VAccess::WRITE},
|
|
|
|
|
"randomize", nullptr};
|
2024-07-26 11:46:30 +02:00
|
|
|
callp->taskp(memberFuncp);
|
|
|
|
|
callp->dtypeFrom(memberFuncp);
|
|
|
|
|
AstVarRef* const basicFvarRefReadp = basicFvarRefp->cloneTree(false);
|
|
|
|
|
basicFvarRefReadp->access(VAccess::READ);
|
|
|
|
|
AstIf* const assignIfNotNullp = new AstIf{
|
|
|
|
|
fl,
|
|
|
|
|
new AstNeq{fl, new AstVarRef{fl, classp, memberVarp, VAccess::READ},
|
|
|
|
|
new AstConst{fl, AstConst::Null{}}},
|
|
|
|
|
new AstAssign{fl, basicFvarRefp->cloneTree(false),
|
|
|
|
|
new AstAnd{fl, basicFvarRefReadp, callp}}};
|
2024-08-21 12:16:44 +02:00
|
|
|
basicRandomizep->addStmtsp(wrapIfRandMode(nodep, memberVarp, assignIfNotNullp));
|
2024-07-26 11:46:30 +02:00
|
|
|
} else {
|
2024-10-08 16:54:20 +02:00
|
|
|
AstVar* const randcVarp = newRandcVarsp(memberVarp);
|
|
|
|
|
AstVarRef* const refp = new AstVarRef{fl, classp, memberVarp, VAccess::WRITE};
|
2024-10-25 18:00:43 +02:00
|
|
|
AstNodeStmt* const stmtp = newRandStmtsp(fl, refp, randcVarp, basicFvarp);
|
2025-09-11 13:01:36 +02:00
|
|
|
if (!refp->backp()) VL_DO_DANGLING(refp->deleteTree(), refp);
|
2025-09-23 20:49:01 +02:00
|
|
|
basicRandomizep->addStmtsp(new AstBegin{fl, "", stmtp, false});
|
2024-07-26 11:46:30 +02:00
|
|
|
}
|
|
|
|
|
});
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-21 12:16:44 +02:00
|
|
|
// Creates a lvalue reference to the randomize mode var. Called by visit(AstNodeFTaskRef*)
|
|
|
|
|
AstNodeExpr* makeModeAssignLhs(FileLine* const fl, AstClass* const classp,
|
|
|
|
|
AstNodeExpr* const fromp, AstVar* const modeVarp) {
|
|
|
|
|
if (classp == m_modp) {
|
|
|
|
|
// Called on 'this' or a member of 'this'
|
|
|
|
|
return new AstVarRef{fl, VN_AS(modeVarp->user2p(), NodeModule), modeVarp,
|
|
|
|
|
VAccess::WRITE};
|
|
|
|
|
} else {
|
|
|
|
|
AstMemberSel* const memberselp = new AstMemberSel{fl, fromp->unlinkFrBack(), modeVarp};
|
|
|
|
|
memberselp->foreach([](AstVarRef* varrefp) { varrefp->access(VAccess::WRITE); });
|
|
|
|
|
return memberselp;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Replace the node with an assignment to the mode variable. Called by visit(AstNodeFTaskRef*)
|
|
|
|
|
void replaceWithModeAssign(AstNodeFTaskRef* const ftaskRefp, AstNode* const receiverp,
|
|
|
|
|
AstNodeExpr* const lhsp) {
|
|
|
|
|
FileLine* const fl = ftaskRefp->fileline();
|
|
|
|
|
if (ftaskRefp->pinsp()) {
|
|
|
|
|
UASSERT_OBJ(VN_IS(ftaskRefp->backp(), StmtExpr), ftaskRefp, "Should be a statement");
|
|
|
|
|
AstNodeExpr* const rhsp = VN_AS(ftaskRefp->pinsp(), Arg)->exprp()->unlinkFrBack();
|
|
|
|
|
if (receiverp) {
|
|
|
|
|
// Called on a rand member variable/constraint. Set the variable/constraint's
|
|
|
|
|
// mode
|
2024-08-24 00:24:34 +02:00
|
|
|
const RandomizeMode rmode = {.asInt = receiverp->user1()};
|
|
|
|
|
UASSERT_OBJ(rmode.usesMode, ftaskRefp, "Failed to set usesMode");
|
2025-09-27 14:22:17 +02:00
|
|
|
AstCMethodHard* const setp = new AstCMethodHard{fl, lhsp, VCMethod::ARRAY_AT_WRITE,
|
|
|
|
|
new AstConst{fl, rmode.index}};
|
2024-09-02 15:45:47 +02:00
|
|
|
setp->dtypeSetUInt32();
|
|
|
|
|
m_stmtp->replaceWith(new AstAssign{fl, setp, rhsp});
|
2024-08-21 12:16:44 +02:00
|
|
|
} else {
|
|
|
|
|
// For rand_mode: Called on 'this' or a non-rand class instance.
|
|
|
|
|
// For constraint_mode: Called on a class instance.
|
|
|
|
|
// Set the rand mode of all members
|
|
|
|
|
m_stmtp->replaceWith(makeModeSetLoop(fl, lhsp, rhsp, m_ftaskp));
|
|
|
|
|
}
|
|
|
|
|
pushDeletep(m_stmtp);
|
|
|
|
|
} else {
|
|
|
|
|
UASSERT_OBJ(receiverp, ftaskRefp, "Should have receiver");
|
2024-08-24 00:24:34 +02:00
|
|
|
const RandomizeMode rmode = {.asInt = receiverp->user1()};
|
|
|
|
|
UASSERT_OBJ(rmode.usesMode, ftaskRefp, "Failed to set usesMode");
|
2025-09-27 14:22:17 +02:00
|
|
|
AstCMethodHard* const setp = new AstCMethodHard{fl, lhsp, VCMethod::ARRAY_AT_WRITE,
|
|
|
|
|
new AstConst{fl, rmode.index}};
|
2024-09-02 15:45:47 +02:00
|
|
|
setp->dtypeSetUInt32();
|
|
|
|
|
ftaskRefp->replaceWith(setp);
|
2024-08-21 12:16:44 +02:00
|
|
|
VL_DO_DANGLING(pushDeletep(ftaskRefp), ftaskRefp);
|
|
|
|
|
}
|
|
|
|
|
};
|
|
|
|
|
|
2024-08-13 20:20:31 +02:00
|
|
|
// Handle inline random variable control. After this, the randomize() call has no args
|
|
|
|
|
void handleRandomizeArgs(AstNodeFTaskRef* const nodep) {
|
|
|
|
|
if (!nodep->pinsp()) return;
|
|
|
|
|
// This assumes arguments to always be a member sel from nodep->fromp(), if applicable
|
|
|
|
|
// e.g. LinkDot transformed a.randomize(b, a.c) -> a.randomize(a.b, a.c)
|
|
|
|
|
// Merge pins with common prefixes so that setting their rand mode doesn't interfere
|
|
|
|
|
// with each other.
|
|
|
|
|
// e.g. a.randomize(a.b, a.c, a.b.d) -> a.randomize(a.b, a.c)
|
|
|
|
|
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
|
|
|
|
|
nextp = pinp->nextp();
|
|
|
|
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
|
|
|
|
if (!argp) continue;
|
|
|
|
|
AstNode* otherNextp = nullptr;
|
|
|
|
|
for (AstNode* otherPinp = nextp; otherPinp; otherPinp = otherNextp) {
|
|
|
|
|
otherNextp = otherPinp->nextp();
|
|
|
|
|
AstArg* const otherArgp = VN_CAST(otherPinp, Arg);
|
|
|
|
|
if (!otherArgp) continue;
|
|
|
|
|
if (AstNodeExpr* const prefixp
|
|
|
|
|
= sliceToCommonPrefix(argp->exprp(), otherArgp->exprp())) {
|
|
|
|
|
if (prefixp == argp->exprp()) {
|
|
|
|
|
if (nextp == otherPinp) nextp = nextp->nextp();
|
|
|
|
|
VL_DO_DANGLING(otherPinp->unlinkFrBack()->deleteTree(), otherPinp);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (AstNodeExpr* const prefixp
|
|
|
|
|
= sliceToCommonPrefix(otherArgp->exprp(), argp->exprp())) {
|
|
|
|
|
if (prefixp == otherArgp->exprp()) {
|
|
|
|
|
VL_DO_DANGLING(pinp->unlinkFrBack()->deleteTree(), pinp);
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
// Construct temp vars, and store and restore statements
|
|
|
|
|
std::set<AstVar*> savedRandModeVarps;
|
|
|
|
|
AstVar* tmpVarps = nullptr;
|
|
|
|
|
AstNode* storeStmtsp = nullptr;
|
|
|
|
|
AstNode* setStmtsp = nullptr;
|
|
|
|
|
AstNodeStmt* restoreStmtsp = nullptr;
|
|
|
|
|
for (AstNode *pinp = nodep->pinsp(), *nextp = nullptr; pinp; pinp = nextp) {
|
|
|
|
|
nextp = pinp->nextp();
|
|
|
|
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
|
|
|
|
if (!argp) continue;
|
|
|
|
|
AstNodeExpr* exprp = VN_AS(pinp, Arg)->exprp();
|
|
|
|
|
AstNodeExpr* const commonPrefixp = sliceToCommonPrefix(exprp, nodep);
|
|
|
|
|
UASSERT_OBJ(commonPrefixp != exprp, nodep,
|
|
|
|
|
"Common prefix should be different than pin");
|
|
|
|
|
FileLine* const fl = argp->fileline();
|
|
|
|
|
while (exprp) {
|
|
|
|
|
if (commonPrefixp == exprp) break;
|
|
|
|
|
AstVar* const randVarp = getVarFromRef(exprp);
|
|
|
|
|
AstClass* const classp = VN_AS(randVarp->user2p(), Class);
|
|
|
|
|
AstVar* const randModeVarp = getRandModeVar(classp);
|
|
|
|
|
if (savedRandModeVarps.find(randModeVarp) == savedRandModeVarps.end()) {
|
|
|
|
|
AstVar* const randModeTmpVarp
|
|
|
|
|
= makeTmpRandModeVar(exprp, randModeVarp, storeStmtsp, restoreStmtsp);
|
|
|
|
|
savedRandModeVarps.insert(randModeVarp);
|
|
|
|
|
tmpVarps = AstNode::addNext(tmpVarps, randModeTmpVarp);
|
|
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
const RandomizeMode randMode = {.asInt = randVarp->user1()};
|
2025-09-27 14:22:17 +02:00
|
|
|
AstCMethodHard* setp = new AstCMethodHard{
|
|
|
|
|
fl, makeSiblingRefp(exprp, randModeVarp, VAccess::WRITE),
|
|
|
|
|
VCMethod::ARRAY_AT_WRITE, new AstConst{fl, randMode.index}};
|
2024-09-02 15:45:47 +02:00
|
|
|
setp->dtypeSetUInt32();
|
2024-08-13 20:20:31 +02:00
|
|
|
setStmtsp
|
2024-09-02 15:45:47 +02:00
|
|
|
= AstNode::addNext(setStmtsp, new AstAssign{fl, setp, new AstConst{fl, 1}});
|
2024-08-13 20:20:31 +02:00
|
|
|
exprp = getFromp(exprp);
|
|
|
|
|
}
|
|
|
|
|
pinp->unlinkFrBack()->deleteTree();
|
|
|
|
|
}
|
|
|
|
|
if (tmpVarps) {
|
|
|
|
|
UASSERT_OBJ(storeStmtsp && setStmtsp && restoreStmtsp, nodep, "Should have stmts");
|
|
|
|
|
VNRelinker relinker;
|
|
|
|
|
m_stmtp->unlinkFrBack(&relinker);
|
|
|
|
|
AstNode* const stmtsp = tmpVarps;
|
|
|
|
|
stmtsp->addNext(storeStmtsp);
|
|
|
|
|
stmtsp->addNext(setStmtsp);
|
|
|
|
|
stmtsp->addNext(m_stmtp);
|
|
|
|
|
stmtsp->addNext(restoreStmtsp);
|
2025-09-23 20:49:01 +02:00
|
|
|
relinker.relink(new AstBegin{nodep->fileline(), "", stmtsp, true});
|
2024-08-13 20:20:31 +02:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
// VISITORS
|
2022-11-12 03:53:05 +01:00
|
|
|
void visit(AstNodeModule* nodep) override {
|
2022-11-19 03:30:24 +01:00
|
|
|
VL_RESTORER(m_modp);
|
2022-11-12 03:53:05 +01:00
|
|
|
VL_RESTORER(m_randCaseNum);
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_randCaseNum = 0;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-11-19 03:30:24 +01:00
|
|
|
void visit(AstNodeFTask* nodep) override {
|
|
|
|
|
VL_RESTORER(m_ftaskp);
|
|
|
|
|
m_ftaskp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstClass* nodep) override {
|
2022-11-19 03:30:24 +01:00
|
|
|
VL_RESTORER(m_modp);
|
2022-11-12 03:53:05 +01:00
|
|
|
VL_RESTORER(m_randCaseNum);
|
|
|
|
|
m_modp = nodep;
|
|
|
|
|
m_randCaseNum = 0;
|
2024-07-12 16:18:18 +02:00
|
|
|
|
2020-12-07 23:55:22 +01:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
if (!nodep->user1()) return; // Doesn't need randomize, or already processed
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(9, "Define randomize() for " << nodep);
|
2024-09-10 15:10:36 +02:00
|
|
|
nodep->baseMostClassp()->needRNG(true);
|
2025-09-19 11:03:56 +02:00
|
|
|
|
2025-10-26 20:03:16 +01:00
|
|
|
const bool globalConstrained = nodep->user1() == IS_RANDOMIZED_GLOBAL;
|
2024-07-17 08:21:45 +02:00
|
|
|
AstFunc* const randomizep = V3Randomize::newRandomizeFunc(m_memberMap, nodep);
|
|
|
|
|
AstVar* const fvarp = VN_AS(randomizep->fvarp(), Var);
|
|
|
|
|
addPrePostCall(nodep, randomizep, "pre_randomize");
|
2023-03-14 14:48:06 +01:00
|
|
|
FileLine* fl = nodep->fileline();
|
2023-03-15 16:48:18 +01:00
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
AstVar* const randModeVarp = getRandModeVar(nodep);
|
2023-03-15 16:48:18 +01:00
|
|
|
AstNodeExpr* beginValp = nullptr;
|
2024-07-19 19:03:48 +02:00
|
|
|
AstVar* genp = getRandomGenerator(nodep);
|
|
|
|
|
if (genp) {
|
|
|
|
|
nodep->foreachMember([&](AstClass* const classp, AstConstraint* const constrp) {
|
|
|
|
|
AstTask* taskp = VN_AS(constrp->user2p(), Task);
|
|
|
|
|
if (!taskp) {
|
|
|
|
|
taskp = newSetupConstraintTask(classp, constrp->name());
|
|
|
|
|
constrp->user2p(taskp);
|
|
|
|
|
}
|
|
|
|
|
AstTaskRef* const setupTaskRefp
|
2024-12-15 15:15:49 +01:00
|
|
|
= new AstTaskRef{constrp->fileline(), taskp, nullptr};
|
2024-07-19 19:03:48 +02:00
|
|
|
setupTaskRefp->classOrPackagep(classp);
|
2024-07-17 08:21:45 +02:00
|
|
|
|
2024-07-19 19:03:48 +02:00
|
|
|
AstTask* const setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
2024-07-17 08:21:45 +02:00
|
|
|
|
2024-07-19 19:03:48 +02:00
|
|
|
setupAllTaskp->addStmtsp(setupTaskRefp->makeStmt());
|
2024-07-17 08:21:45 +02:00
|
|
|
|
2024-11-15 16:45:06 +01:00
|
|
|
if (AstTask* const resizeTaskp = VN_CAST(constrp->user3p(), Task)) {
|
|
|
|
|
AstTask* const resizeAllTaskp = getCreateAggrResizeTask(nodep);
|
|
|
|
|
AstTaskRef* const resizeTaskRefp
|
2024-12-15 15:15:49 +01:00
|
|
|
= new AstTaskRef{constrp->fileline(), resizeTaskp, nullptr};
|
2024-11-15 16:45:06 +01:00
|
|
|
resizeTaskRefp->classOrPackagep(classp);
|
|
|
|
|
resizeAllTaskp->addStmtsp(resizeTaskRefp->makeStmt());
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
ConstraintExprVisitor{m_memberMap, constrp->itemsp(), nullptr, genp, randModeVarp};
|
2024-08-21 12:16:44 +02:00
|
|
|
if (constrp->itemsp()) {
|
|
|
|
|
taskp->addStmtsp(wrapIfConstraintMode(
|
|
|
|
|
nodep, constrp, constrp->itemsp()->unlinkFrBackWithNext()));
|
|
|
|
|
}
|
2024-07-19 19:03:48 +02:00
|
|
|
});
|
2024-07-17 08:21:45 +02:00
|
|
|
randomizep->addStmtsp(implementConstraintsClear(fl, genp));
|
|
|
|
|
AstTask* setupAllTaskp = getCreateConstraintSetupFunc(nodep);
|
2024-12-15 15:15:49 +01:00
|
|
|
AstTaskRef* const setupTaskRefp = new AstTaskRef{fl, setupAllTaskp, nullptr};
|
2024-07-17 08:21:45 +02:00
|
|
|
randomizep->addStmtsp(setupTaskRefp->makeStmt());
|
|
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
AstNodeModule* const genModp = VN_AS(genp->user2p(), NodeModule);
|
|
|
|
|
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCExpr* const solverCallp = new AstCExpr{fl};
|
2024-05-17 16:38:34 +02:00
|
|
|
solverCallp->dtypeSetBit();
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
solverCallp->add(new AstVarRef{fl, genModp, genp, VAccess::READWRITE});
|
|
|
|
|
solverCallp->add(".next(__Vm_rng)");
|
2024-07-17 08:21:45 +02:00
|
|
|
beginValp = solverCallp;
|
2024-07-31 23:30:48 +02:00
|
|
|
if (randModeVarp) {
|
|
|
|
|
AstNodeModule* const randModeClassp = VN_AS(randModeVarp->user2p(), Class);
|
|
|
|
|
AstNodeFTask* const newp
|
|
|
|
|
= VN_AS(m_memberMap.findMember(randModeClassp, "new"), NodeFTask);
|
|
|
|
|
UASSERT_OBJ(newp, randModeClassp, "No new() in class");
|
|
|
|
|
addSetRandMode(newp, genp, randModeVarp);
|
|
|
|
|
}
|
2024-07-17 08:21:45 +02:00
|
|
|
} else {
|
|
|
|
|
beginValp = new AstConst{fl, AstConst::WidthedValue{}, 32, 1};
|
2024-05-17 16:38:34 +02:00
|
|
|
}
|
2023-03-15 16:48:18 +01:00
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVarRef* const fvarRefp = new AstVarRef{fl, fvarp, VAccess::WRITE};
|
2025-10-11 17:44:20 +02:00
|
|
|
|
|
|
|
|
// For global constraints: call basic randomize first (without global constraints)
|
2025-10-19 11:55:59 +02:00
|
|
|
if (globalConstrained) {
|
2025-10-11 17:44:20 +02:00
|
|
|
AstFunc* const basicRandomizep
|
|
|
|
|
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME);
|
|
|
|
|
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
|
|
|
|
|
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
|
|
|
|
|
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, basicRandomizeCallp});
|
|
|
|
|
} else {
|
|
|
|
|
// For normal classes: use beginValp (standard flow)
|
|
|
|
|
randomizep->addStmtsp(new AstAssign{fl, fvarRefp, beginValp});
|
|
|
|
|
}
|
2023-03-15 16:48:18 +01:00
|
|
|
|
2024-11-15 16:45:06 +01:00
|
|
|
if (AstTask* const resizeAllTaskp
|
|
|
|
|
= VN_AS(m_memberMap.findMember(nodep, "__Vresize_constrained_arrays"), Task)) {
|
2024-12-15 15:15:49 +01:00
|
|
|
AstTaskRef* const resizeTaskRefp = new AstTaskRef{fl, resizeAllTaskp, nullptr};
|
2024-11-15 16:45:06 +01:00
|
|
|
randomizep->addStmtsp(resizeTaskRefp->makeStmt());
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-26 11:46:30 +02:00
|
|
|
AstVarRef* const fvarRefReadp = fvarRefp->cloneTree(false);
|
|
|
|
|
fvarRefReadp->access(VAccess::READ);
|
|
|
|
|
|
2025-10-11 17:44:20 +02:00
|
|
|
// For global constraints: combine with solver result (beginValp)
|
|
|
|
|
// For normal classes: call basic randomize after resize
|
2025-10-19 11:55:59 +02:00
|
|
|
if (globalConstrained) {
|
2025-10-11 17:44:20 +02:00
|
|
|
randomizep->addStmtsp(new AstAssign{fl, fvarRefp->cloneTree(false),
|
|
|
|
|
new AstAnd{fl, fvarRefReadp, beginValp}});
|
|
|
|
|
} else {
|
|
|
|
|
AstFunc* const basicRandomizep
|
|
|
|
|
= V3Randomize::newRandomizeFunc(m_memberMap, nodep, BASIC_RANDOMIZE_FUNC_NAME);
|
|
|
|
|
addBasicRandomizeBody(basicRandomizep, nodep, randModeVarp);
|
|
|
|
|
AstFuncRef* const basicRandomizeCallp = new AstFuncRef{fl, basicRandomizep, nullptr};
|
2025-10-11 17:57:28 +02:00
|
|
|
randomizep->addStmtsp(
|
|
|
|
|
new AstAssign{fl, fvarRefp->cloneTree(false),
|
|
|
|
|
new AstAnd{fl, fvarRefReadp, basicRandomizeCallp}});
|
2025-10-11 17:44:20 +02:00
|
|
|
}
|
2024-07-17 08:21:45 +02:00
|
|
|
addPrePostCall(nodep, randomizep, "post_randomize");
|
2020-12-07 23:55:22 +01:00
|
|
|
nodep->user1(false);
|
|
|
|
|
}
|
2022-11-12 03:53:05 +01:00
|
|
|
void visit(AstRandCase* nodep) override {
|
|
|
|
|
// RANDCASE
|
|
|
|
|
// CASEITEM expr1 : stmt1
|
|
|
|
|
// CASEITEM expr2 : stmt2
|
|
|
|
|
// ->
|
|
|
|
|
// tmp = URandomRange{0, num} + 1 // + 1 so weight 0 means never
|
|
|
|
|
// if (tmp < expr1) stmt1;
|
|
|
|
|
// else if (tmp < (expr2 + expr1)) stmt1;
|
|
|
|
|
// else warning
|
|
|
|
|
// Note this code assumes that the expressions after V3Const are fast to compute
|
|
|
|
|
// Optimize: we would be better with a binary search tree to reduce ifs that execute
|
2025-08-02 19:44:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "rcin:");
|
2022-11-12 03:53:05 +01:00
|
|
|
AstNodeDType* const sumDTypep = nodep->findUInt64DType();
|
|
|
|
|
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
|
|
|
|
const std::string name = "__Vrandcase" + cvtToStr(m_randCaseNum++);
|
2022-11-19 03:30:24 +01:00
|
|
|
AstVar* const randVarp = new AstVar{fl, VVarType::BLOCKTEMP, name, sumDTypep};
|
2022-11-12 03:53:05 +01:00
|
|
|
randVarp->noSubst(true);
|
2022-11-19 03:30:24 +01:00
|
|
|
if (m_ftaskp) randVarp->funcLocal(true);
|
2022-11-12 03:53:05 +01:00
|
|
|
AstNodeExpr* sump = new AstConst{fl, AstConst::WidthedValue{}, 64, 0};
|
2024-07-12 16:18:18 +02:00
|
|
|
AstNodeIf* const firstIfsp
|
2022-11-12 03:53:05 +01:00
|
|
|
= new AstIf{fl, new AstConst{fl, AstConst::BitFalse{}}, nullptr, nullptr};
|
|
|
|
|
AstNodeIf* ifsp = firstIfsp;
|
|
|
|
|
|
|
|
|
|
for (AstCaseItem* itemp = nodep->itemsp(); itemp;
|
|
|
|
|
itemp = VN_AS(itemp->nextp(), CaseItem)) {
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* const condp = itemp->condsp()->unlinkFrBack();
|
2022-11-12 03:53:05 +01:00
|
|
|
sump
|
|
|
|
|
= new AstAdd{condp->fileline(), sump, new AstExtend{itemp->fileline(), condp, 64}};
|
|
|
|
|
AstNode* const stmtsp
|
|
|
|
|
= itemp->stmtsp() ? itemp->stmtsp()->unlinkFrBackWithNext() : nullptr;
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVarRef* const randVarRefp = new AstVarRef{fl, randVarp, VAccess::WRITE};
|
2022-11-12 03:53:05 +01:00
|
|
|
AstNodeIf* const newifp
|
|
|
|
|
= new AstIf{itemp->fileline(),
|
2024-07-12 16:18:18 +02:00
|
|
|
new AstLte{condp->fileline(), randVarRefp, sump->cloneTreePure(true)},
|
2022-11-12 03:53:05 +01:00
|
|
|
stmtsp, nullptr};
|
|
|
|
|
ifsp->addElsesp(newifp);
|
|
|
|
|
ifsp = newifp;
|
|
|
|
|
}
|
|
|
|
|
AstDisplay* dispp = new AstDisplay{
|
2024-03-02 15:05:21 +01:00
|
|
|
fl, VDisplayType::DT_ERROR, "All randcase items had 0 weights (IEEE 1800-2023 18.16)",
|
2022-11-12 03:53:05 +01:00
|
|
|
nullptr, nullptr};
|
|
|
|
|
UASSERT_OBJ(m_modp, nodep, "randcase not under module");
|
|
|
|
|
dispp->fmtp()->timeunit(m_modp->timeunit());
|
|
|
|
|
ifsp->addElsesp(dispp);
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstNode* const newp = randVarp;
|
2022-11-13 21:33:11 +01:00
|
|
|
AstNodeExpr* randp = new AstRand{fl, nullptr, false};
|
2022-11-12 03:53:05 +01:00
|
|
|
randp->dtypeSetUInt64();
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVarRef* const randVarRefp = new AstVarRef{fl, randVarp, VAccess::WRITE};
|
|
|
|
|
newp->addNext(new AstAssign{fl, randVarRefp,
|
2022-11-12 03:53:05 +01:00
|
|
|
new AstAdd{fl, new AstConst{fl, AstConst::Unsized64{}, 1},
|
|
|
|
|
new AstModDiv{fl, randp, sump}}});
|
|
|
|
|
newp->addNext(firstIfsp);
|
2022-11-27 14:31:22 +01:00
|
|
|
if (debug() >= 9) newp->dumpTreeAndNext(cout, "- rcnew: ");
|
2022-11-12 03:53:05 +01:00
|
|
|
nodep->replaceWith(newp);
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
|
|
|
}
|
2024-07-19 09:14:56 +02:00
|
|
|
void visit(AstNodeFTaskRef* nodep) override {
|
2024-07-31 23:30:48 +02:00
|
|
|
if (nodep->name() == "rand_mode") {
|
|
|
|
|
AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall);
|
|
|
|
|
AstNodeExpr* const fromp = methodCallp ? methodCallp->fromp() : nullptr;
|
|
|
|
|
const RandModeTarget randModeTarget = RandModeTarget::get(fromp, m_modp);
|
|
|
|
|
UASSERT_OBJ(randModeTarget.classp, nodep,
|
|
|
|
|
"Should have checked in RandomizeMarkVisitor");
|
2024-08-21 12:16:44 +02:00
|
|
|
AstVar* const receiverp = randModeTarget.receiverp;
|
2024-07-31 23:30:48 +02:00
|
|
|
AstVar* const randModeVarp = getRandModeVar(randModeTarget.classp);
|
2024-08-21 12:16:44 +02:00
|
|
|
AstNodeExpr* const lhsp = makeModeAssignLhs(nodep->fileline(), randModeTarget.classp,
|
|
|
|
|
randModeTarget.fromp, randModeVarp);
|
|
|
|
|
replaceWithModeAssign(nodep,
|
|
|
|
|
// If the receiver is not rand, set the rand_mode for all members
|
|
|
|
|
receiverp && receiverp->rand().isRand() ? receiverp : nullptr,
|
|
|
|
|
lhsp);
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (nodep->name() == "constraint_mode") {
|
|
|
|
|
AstMethodCall* const methodCallp = VN_CAST(nodep, MethodCall);
|
|
|
|
|
AstNodeExpr* fromp = methodCallp ? methodCallp->fromp() : nullptr;
|
|
|
|
|
AstConstraint* constrp = nullptr;
|
|
|
|
|
AstClass* classp = VN_CAST(m_modp, Class);
|
|
|
|
|
if (AstConstraintRef* const constrRefp = VN_CAST(fromp, ConstraintRef)) {
|
|
|
|
|
constrp = constrRefp->constrp();
|
|
|
|
|
if (constrRefp->fromp()) {
|
|
|
|
|
fromp = constrRefp->fromp();
|
|
|
|
|
classp = VN_AS(fromp->dtypep()->skipRefp(), ClassRefDType)->classp();
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
} else if (fromp) {
|
|
|
|
|
classp = VN_AS(fromp->dtypep()->skipRefp(), ClassRefDType)->classp();
|
2024-07-31 23:30:48 +02:00
|
|
|
}
|
2024-08-21 12:16:44 +02:00
|
|
|
UASSERT_OBJ(classp, nodep, "Failed to find class");
|
|
|
|
|
AstVar* const constraintModeVarp = getConstraintModeVar(classp);
|
|
|
|
|
AstNodeExpr* const lhsp
|
|
|
|
|
= makeModeAssignLhs(nodep->fileline(), classp, fromp, constraintModeVarp);
|
|
|
|
|
replaceWithModeAssign(nodep, constrp, lhsp);
|
2024-07-31 23:30:48 +02:00
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-08-13 20:20:31 +02:00
|
|
|
if (nodep->name() != "randomize") return;
|
|
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
if (nodep->classOrPackagep() && nodep->classOrPackagep()->name() == "std") {
|
2025-08-28 03:25:40 +02:00
|
|
|
// Handle std::randomize; create wrapper function that calls basicStdRandomization on
|
|
|
|
|
// each varref argument, then transform nodep to call that wrapper
|
2025-07-25 12:13:46 +02:00
|
|
|
AstVar* const stdrand = createStdRandomGenerator(m_modp);
|
|
|
|
|
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeStdFunc(
|
|
|
|
|
m_memberMap, m_modp, m_inlineUniqueStdName.get(nodep));
|
|
|
|
|
randomizeFuncp->addStmtsp(
|
|
|
|
|
new AstAssign{nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
|
|
|
|
|
VAccess::WRITE},
|
|
|
|
|
new AstConst{nodep->fileline(), AstConst::WidthedValue{}, 32, 1}});
|
2025-08-28 03:25:40 +02:00
|
|
|
int argn = 0;
|
2025-07-25 12:13:46 +02:00
|
|
|
for (AstNode* pinp = nodep->pinsp(); pinp; pinp = pinp->nextp()) {
|
|
|
|
|
AstArg* const argp = VN_CAST(pinp, Arg);
|
|
|
|
|
if (!argp) continue;
|
|
|
|
|
AstNodeExpr* exprp = argp->exprp();
|
2025-08-28 03:25:40 +02:00
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
AstCMethodHard* const basicMethodp = new AstCMethodHard{
|
|
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(), stdrand, VAccess::READWRITE},
|
2025-09-27 14:22:17 +02:00
|
|
|
VCMethod::RANDOMIZER_BASIC_STD_RANDOMIZATION};
|
2025-08-28 03:25:40 +02:00
|
|
|
AstVar* const refvarp
|
|
|
|
|
= new AstVar{exprp->fileline(), VVarType::MEMBER,
|
|
|
|
|
"__Varg"s + std::to_string(++argn), exprp->dtypep()};
|
|
|
|
|
refvarp->direction(VDirection::REF);
|
|
|
|
|
refvarp->funcLocal(true);
|
2025-09-22 01:52:19 +02:00
|
|
|
refvarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2025-08-28 03:25:40 +02:00
|
|
|
randomizeFuncp->addStmtsp(refvarp);
|
|
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
const size_t width = exprp->width();
|
2025-08-28 03:25:40 +02:00
|
|
|
basicMethodp->addPinsp(
|
|
|
|
|
new AstVarRef{exprp->fileline(), refvarp, VAccess::READWRITE});
|
|
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
basicMethodp->addPinsp(
|
|
|
|
|
new AstConst{nodep->fileline(), AstConst::Unsized64{}, width});
|
|
|
|
|
basicMethodp->dtypeSetBit();
|
2025-08-28 03:25:40 +02:00
|
|
|
|
2025-07-25 12:13:46 +02:00
|
|
|
randomizeFuncp->addStmtsp(new AstAssign{
|
|
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var),
|
|
|
|
|
VAccess::WRITE},
|
|
|
|
|
new AstAnd{nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(),
|
|
|
|
|
VN_AS(randomizeFuncp->fvarp(), Var), VAccess::READ},
|
|
|
|
|
basicMethodp}});
|
|
|
|
|
}
|
|
|
|
|
// Replace the node with a call to that function
|
|
|
|
|
nodep->name(randomizeFuncp->name());
|
|
|
|
|
nodep->taskp(randomizeFuncp);
|
|
|
|
|
nodep->dtypeFrom(randomizeFuncp->dtypep());
|
|
|
|
|
if (VN_IS(m_modp, Class)) nodep->classOrPackagep(m_modp);
|
2025-08-28 03:25:40 +02:00
|
|
|
UINFOTREE(9, nodep, "", "std::rnd-call");
|
|
|
|
|
UINFOTREE(9, randomizeFuncp, "", "std::rnd-func");
|
2025-07-25 12:13:46 +02:00
|
|
|
return;
|
|
|
|
|
}
|
2024-08-13 20:20:31 +02:00
|
|
|
handleRandomizeArgs(nodep);
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
AstWith* const withp = VN_CAST(nodep->pinsp(), With);
|
2024-08-13 20:20:31 +02:00
|
|
|
if (!withp) {
|
2024-07-12 16:18:18 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
return;
|
|
|
|
|
}
|
2024-07-19 09:14:56 +02:00
|
|
|
withp->unlinkFrBack();
|
2024-07-12 16:18:18 +02:00
|
|
|
iterateChildren(nodep);
|
|
|
|
|
|
2024-07-19 09:14:56 +02:00
|
|
|
AstClass* classp = nullptr;
|
|
|
|
|
if (AstMethodCall* const callp = VN_CAST(nodep, MethodCall)) {
|
2025-07-18 13:04:47 +02:00
|
|
|
const AstNodeDType* const fromDTypep = callp->fromp()->dtypep();
|
|
|
|
|
UASSERT_OBJ(fromDTypep, callp->fromp(), "Object dtype is not linked");
|
|
|
|
|
const AstClassRefDType* const classrefdtypep
|
|
|
|
|
= VN_CAST(fromDTypep->skipRefp(), ClassRefDType);
|
|
|
|
|
UASSERT_OBJ(classrefdtypep, callp->fromp(),
|
|
|
|
|
"Randomize called on expression of non-class type "
|
|
|
|
|
<< fromDTypep->skipRefp()->prettyDTypeNameQ()
|
|
|
|
|
<< " (it should be detected earlier)");
|
2024-07-19 09:14:56 +02:00
|
|
|
classp = classrefdtypep->classp();
|
|
|
|
|
UASSERT_OBJ(classp, classrefdtypep, "Class type is unlinked to its ref type");
|
|
|
|
|
} else {
|
|
|
|
|
classp = VN_CAST(m_modp, Class);
|
|
|
|
|
UASSERT_OBJ(classp, m_modp, "Module not class, should have failed in V3Width");
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
|
|
|
|
if (classp->user1()) {
|
2024-07-19 19:03:48 +02:00
|
|
|
// We need to first ensure that the class constraints are transformed
|
2024-07-12 16:18:18 +02:00
|
|
|
// NOTE: This is safe only because AstClass visit function overwrites all
|
|
|
|
|
// nesting-dependent state variables
|
|
|
|
|
iterate(classp);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-19 19:03:48 +02:00
|
|
|
AstVar* const classGenp = getRandomGenerator(classp);
|
2024-07-12 16:18:18 +02:00
|
|
|
AstVar* const localGenp
|
|
|
|
|
= new AstVar{nodep->fileline(), VVarType::BLOCKTEMP, "randomizer",
|
|
|
|
|
classp->findBasicDType(VBasicDTypeKwd::RANDOM_GENERATOR)};
|
|
|
|
|
localGenp->funcLocal(true);
|
|
|
|
|
|
2024-08-02 17:45:17 +02:00
|
|
|
AstFunc* const randomizeFuncp = V3Randomize::newRandomizeFunc(
|
|
|
|
|
m_memberMap, classp, m_inlineUniqueNames.get(nodep), false);
|
2024-07-12 16:18:18 +02:00
|
|
|
|
2024-08-28 19:42:49 +02:00
|
|
|
addPrePostCall(classp, randomizeFuncp, "pre_randomize");
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
// Detach the expression and prepare variable copies
|
2024-08-20 19:25:58 +02:00
|
|
|
const CaptureVisitor captured{withp->exprp(), m_modp, classp};
|
2024-07-12 16:18:18 +02:00
|
|
|
// Add function arguments
|
2024-08-02 17:45:17 +02:00
|
|
|
captured.addFunctionArguments(randomizeFuncp);
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
// Add constraints clearing code
|
|
|
|
|
if (classGenp) {
|
|
|
|
|
randomizeFuncp->addStmtsp(
|
|
|
|
|
implementConstraintsClear(randomizeFuncp->fileline(), classGenp));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
randomizeFuncp->addStmtsp(localGenp);
|
|
|
|
|
|
2024-07-26 11:46:30 +02:00
|
|
|
AstFunc* const basicRandomizeFuncp
|
2025-09-19 11:03:56 +02:00
|
|
|
= V3Randomize::newRandomizeFunc(m_memberMap, classp, BASIC_RANDOMIZE_FUNC_NAME);
|
2024-07-26 11:46:30 +02:00
|
|
|
AstFuncRef* const basicRandomizeFuncCallp
|
2024-12-15 15:15:49 +01:00
|
|
|
= new AstFuncRef{nodep->fileline(), basicRandomizeFuncp, nullptr};
|
2024-07-26 11:46:30 +02:00
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
// Copy (derive) class constraints if present
|
|
|
|
|
if (classGenp) {
|
2024-07-17 08:21:45 +02:00
|
|
|
AstTask* const constrSetupFuncp = getCreateConstraintSetupFunc(classp);
|
2024-12-15 15:15:49 +01:00
|
|
|
AstTaskRef* const callp = new AstTaskRef{nodep->fileline(), constrSetupFuncp, nullptr};
|
2024-07-17 08:21:45 +02:00
|
|
|
randomizeFuncp->addStmtsp(callp->makeStmt());
|
2024-07-12 16:18:18 +02:00
|
|
|
randomizeFuncp->addStmtsp(new AstAssign{
|
|
|
|
|
nodep->fileline(), new AstVarRef{nodep->fileline(), localGenp, VAccess::WRITE},
|
2024-07-17 08:21:45 +02:00
|
|
|
new AstVarRef{nodep->fileline(), VN_AS(classGenp->user2p(), NodeModule), classGenp,
|
|
|
|
|
VAccess::READ}});
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-31 23:30:48 +02:00
|
|
|
// Set rand mode if present (not needed if classGenp exists and was copied)
|
|
|
|
|
AstVar* const randModeVarp = getRandModeVar(classp);
|
|
|
|
|
if (!classGenp && randModeVarp) addSetRandMode(randomizeFuncp, localGenp, randModeVarp);
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
// Generate constraint setup code and a hardcoded call to the solver
|
2024-08-02 17:45:17 +02:00
|
|
|
AstNode* const capturedTreep = withp->exprp()->unlinkFrBackWithNext();
|
|
|
|
|
randomizeFuncp->addStmtsp(capturedTreep);
|
|
|
|
|
{
|
|
|
|
|
ConstraintExprVisitor{m_memberMap, capturedTreep, randomizeFuncp, localGenp,
|
|
|
|
|
randModeVarp};
|
|
|
|
|
}
|
2024-07-12 16:18:18 +02:00
|
|
|
|
|
|
|
|
// Call the solver and set return value
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
AstCExpr* const solverCallp = new AstCExpr{nodep->fileline()};
|
2024-07-12 16:18:18 +02:00
|
|
|
solverCallp->dtypeSetBit();
|
Internals: Refactor text based Ast constructs (#6280) (#6571)
Remove the large variety of ways raw "text" is represented in the Ast.
Particularly, the only thing that represents a string to be emitted in
the output is AstText.
There are 5 AstNodes that can contain AstText, and V3Emit will throw an
error if an AstText is encountered anywhere else:
- AstCStmt: Internally generated procedural statements involving raw
text.
- AstCStmtUser: This is the old AstUCStmt, renamed so it sorts next to
AstCStmt, as it's largely equivalent. We should never create this
internally unless used to represent user input. It is used for $c,
statements in the input, and for some 'systemc_* blocks.
- AstCExpr: Internally generaged expression involving raw text.
- AstCExprUser: This is the old AstUCFunc, renamed so it sorts next to
AstCExpr. It is largely equivalent, but also has more optimizations
disabled. This should never be created internally, it is only used for
$c expressions in the input.
- AstTextBlock: Use by V3ProtectLib only, to generate the hierarchical
wrappers.
Text "tracking" for indentation is always on for AstCStmt, AstCExpr, and
AstTextBlock, as these are always generated by us, and should always be
well formed.
Tracking is always off for AstCStmtUser and AstCExprUser, as these
contain arbitrary user input that might not be safe to parse for
indentation.
Remove subsequently redundant AstNodeSimpleText and AstNodeText types.
This patch also fixes incorrect indentation in emitted waveform tracing
functions, and makes the output more readable for hier block SV stubs.
With that, all raw text nodes are handled as a proper AstNodeStmt or
AstNodeExpr as required for #6280.
2025-10-21 13:41:29 +02:00
|
|
|
solverCallp->add(new AstVarRef{nodep->fileline(), localGenp, VAccess::READWRITE});
|
|
|
|
|
solverCallp->add(".next(__Vm_rng)");
|
2024-07-12 16:18:18 +02:00
|
|
|
randomizeFuncp->addStmtsp(new AstAssign{
|
|
|
|
|
nodep->fileline(),
|
|
|
|
|
new AstVarRef{nodep->fileline(), VN_AS(randomizeFuncp->fvarp(), Var), VAccess::WRITE},
|
2024-07-26 11:46:30 +02:00
|
|
|
new AstAnd{nodep->fileline(), basicRandomizeFuncCallp, solverCallp}});
|
2024-07-12 16:18:18 +02:00
|
|
|
|
2024-08-28 19:42:49 +02:00
|
|
|
addPrePostCall(classp, randomizeFuncp, "post_randomize");
|
|
|
|
|
|
2024-07-12 16:18:18 +02:00
|
|
|
// Replace the node with a call to that function
|
2024-07-19 09:14:56 +02:00
|
|
|
nodep->name(randomizeFuncp->name());
|
|
|
|
|
nodep->addPinsp(captured.getArgs());
|
|
|
|
|
nodep->taskp(randomizeFuncp);
|
|
|
|
|
nodep->dtypeFrom(randomizeFuncp->dtypep());
|
|
|
|
|
nodep->classOrPackagep(classp);
|
2024-07-12 16:18:18 +02:00
|
|
|
UINFO(9, "Added `%s` randomization procedure");
|
2024-07-19 09:14:56 +02:00
|
|
|
VL_DO_DANGLING(withp->deleteTree(), withp);
|
2024-07-12 16:18:18 +02:00
|
|
|
}
|
2024-11-15 16:45:06 +01:00
|
|
|
void visit(AstConstraint* nodep) override {
|
|
|
|
|
VL_RESTORER(m_constraintp);
|
|
|
|
|
m_constraintp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
|
|
|
|
void visit(AstCMethodHard* nodep) override {
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
FileLine* const fl = nodep->fileline();
|
2025-09-27 14:22:17 +02:00
|
|
|
if (m_constraintp && nodep->fromp()->user1()
|
|
|
|
|
&& (nodep->method() == VCMethod::ASSOC_SIZE
|
|
|
|
|
|| nodep->method() == VCMethod::DYN_SIZE)) {
|
2024-11-15 16:45:06 +01:00
|
|
|
AstClass* const classp = VN_AS(m_modp, Class);
|
|
|
|
|
AstVarRef* const queueVarRefp = VN_CAST(nodep->fromp(), VarRef);
|
|
|
|
|
if (!queueVarRefp) {
|
|
|
|
|
// Warning from ConstraintExprVisitor will be thrown
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
AstVar* const queueVarp = queueVarRefp->varp();
|
|
|
|
|
AstVar* sizeVarp = VN_CAST(queueVarp->user4p(), Var);
|
|
|
|
|
if (!sizeVarp) {
|
|
|
|
|
sizeVarp = new AstVar{fl, VVarType::BLOCKTEMP, "__V" + queueVarp->name() + "_size",
|
|
|
|
|
nodep->findSigned32DType()};
|
|
|
|
|
classp->addMembersp(sizeVarp);
|
|
|
|
|
m_memberMap.insert(classp, sizeVarp);
|
|
|
|
|
sizeVarp->user2p(classp);
|
|
|
|
|
|
|
|
|
|
queueVarp->user4p(sizeVarp);
|
|
|
|
|
|
|
|
|
|
AstTask* resizerTaskp = VN_AS(m_constraintp->user3p(), Task);
|
|
|
|
|
if (!resizerTaskp) {
|
|
|
|
|
resizerTaskp = newResizeConstrainedArrayTask(classp, m_constraintp->name());
|
|
|
|
|
m_constraintp->user3p(resizerTaskp);
|
|
|
|
|
}
|
|
|
|
|
AstCMethodHard* const resizep
|
2025-09-27 14:22:17 +02:00
|
|
|
= new AstCMethodHard{fl, nodep->fromp()->unlinkFrBack(), VCMethod::DYN_RESIZE,
|
2024-11-15 16:45:06 +01:00
|
|
|
new AstVarRef{fl, sizeVarp, VAccess::READ}};
|
|
|
|
|
resizep->dtypep(nodep->findVoidDType());
|
|
|
|
|
resizerTaskp->addStmtsp(new AstStmtExpr{fl, resizep});
|
|
|
|
|
|
|
|
|
|
// Since size variable is signed int, we need additional constraint
|
|
|
|
|
// to make sure it is always >= 0.
|
|
|
|
|
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
|
|
|
|
sizeVarRefp->user1(true);
|
|
|
|
|
AstGteS* const sizeGtep = new AstGteS{fl, sizeVarRefp, new AstConst{fl, 0}};
|
|
|
|
|
sizeGtep->user1(true);
|
|
|
|
|
m_constraintp->addItemsp(new AstConstraintExpr{fl, sizeGtep});
|
|
|
|
|
}
|
|
|
|
|
AstVarRef* const sizeVarRefp = new AstVarRef{fl, sizeVarp, VAccess::READ};
|
|
|
|
|
sizeVarRefp->user1(true);
|
|
|
|
|
nodep->replaceWith(sizeVarRefp);
|
|
|
|
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
|
|
|
|
}
|
|
|
|
|
}
|
2024-07-31 23:30:48 +02:00
|
|
|
void visit(AstNodeStmt* nodep) override {
|
|
|
|
|
VL_RESTORER(m_stmtp);
|
|
|
|
|
m_stmtp = nodep;
|
|
|
|
|
iterateChildren(nodep);
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
2020-12-07 23:55:22 +01:00
|
|
|
|
|
|
|
|
public:
|
|
|
|
|
// CONSTRUCTORS
|
2024-07-12 16:18:18 +02:00
|
|
|
explicit RandomizeVisitor(AstNetlist* nodep)
|
2025-07-04 00:59:32 +02:00
|
|
|
: m_inlineUniqueNames{"__Vrandwith"} {
|
2024-07-19 19:03:48 +02:00
|
|
|
createRandomizeClassVars(nodep);
|
2024-07-12 16:18:18 +02:00
|
|
|
iterate(nodep);
|
|
|
|
|
nodep->foreach([&](AstConstraint* constrp) {
|
|
|
|
|
VL_DO_DANGLING(pushDeletep(constrp->unlinkFrBack()), constrp);
|
|
|
|
|
});
|
|
|
|
|
}
|
2022-09-16 12:22:11 +02:00
|
|
|
~RandomizeVisitor() override = default;
|
2020-12-07 23:55:22 +01:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
//######################################################################
|
|
|
|
|
// Randomize method class functions
|
|
|
|
|
|
|
|
|
|
void V3Randomize::randomizeNetlist(AstNetlist* nodep) {
|
2025-05-23 02:29:32 +02:00
|
|
|
UINFO(2, __FUNCTION__ << ":");
|
2020-12-07 23:55:22 +01:00
|
|
|
{
|
2021-11-26 23:55:36 +01:00
|
|
|
const RandomizeMarkVisitor markVisitor{nodep};
|
2024-07-12 16:18:18 +02:00
|
|
|
RandomizeVisitor randomizeVisitor{nodep};
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
2024-01-09 16:35:13 +01:00
|
|
|
V3Global::dumpCheckGlobalTree("randomize", 0, dumpTreeEitherLevel() >= 3);
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
|
2024-07-15 16:42:41 +02:00
|
|
|
AstFunc* V3Randomize::newRandomizeFunc(VMemberMap& memberMap, AstClass* nodep,
|
2024-09-10 15:10:36 +02:00
|
|
|
const std::string& name, bool allowVirtual,
|
|
|
|
|
bool childDType) {
|
2024-07-12 16:18:18 +02:00
|
|
|
AstFunc* funcp = VN_AS(memberMap.findMember(nodep, name), Func);
|
2020-12-07 23:55:22 +01:00
|
|
|
if (!funcp) {
|
2023-04-01 16:50:27 +02:00
|
|
|
v3Global.useRandomizeMethods(true);
|
2022-11-13 17:23:57 +01:00
|
|
|
AstNodeDType* const dtypep
|
2024-09-10 15:10:36 +02:00
|
|
|
= childDType
|
|
|
|
|
? new AstBasicDType{nodep->fileline(), VBasicDTypeKwd::INT}
|
|
|
|
|
: nodep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says int return of 0/1
|
|
|
|
|
AstVar* const fvarp = childDType
|
|
|
|
|
? new AstVar{nodep->fileline(), VVarType::MEMBER, name,
|
|
|
|
|
VFlagChildDType{}, dtypep}
|
|
|
|
|
: new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep};
|
2025-10-08 03:06:11 +02:00
|
|
|
fvarp->fileline()->warnOff(V3ErrorCode::NORETURN, true);
|
2025-09-22 01:52:19 +02:00
|
|
|
fvarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2020-12-07 23:55:22 +01:00
|
|
|
fvarp->funcLocal(true);
|
|
|
|
|
fvarp->funcReturn(true);
|
|
|
|
|
fvarp->direction(VDirection::OUTPUT);
|
2024-07-12 16:18:18 +02:00
|
|
|
nodep->addMembersp(funcp);
|
|
|
|
|
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
|
2024-09-10 15:10:36 +02:00
|
|
|
if (!childDType) funcp->dtypep(dtypep);
|
2020-12-07 23:55:22 +01:00
|
|
|
funcp->classMethod(true);
|
2024-08-02 17:45:17 +02:00
|
|
|
funcp->isVirtual(allowVirtual && nodep->isExtended());
|
2020-12-07 23:55:22 +01:00
|
|
|
nodep->addMembersp(funcp);
|
2024-07-15 16:42:41 +02:00
|
|
|
memberMap.insert(nodep, funcp);
|
2023-04-01 16:50:27 +02:00
|
|
|
}
|
|
|
|
|
return funcp;
|
2025-07-25 12:13:46 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AstFunc* V3Randomize::newRandomizeStdFunc(VMemberMap& memberMap, AstNodeModule* nodep,
|
|
|
|
|
const std::string& name) {
|
|
|
|
|
AstFunc* funcp = nullptr;
|
|
|
|
|
v3Global.useRandomizeMethods(true);
|
|
|
|
|
AstNodeDType* const dtypep = nodep->findBitDType(32, 32, VSigning::SIGNED);
|
|
|
|
|
AstVar* const fvarp = new AstVar{nodep->fileline(), VVarType::MEMBER, name, dtypep};
|
2025-09-22 01:52:19 +02:00
|
|
|
fvarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2025-07-25 12:13:46 +02:00
|
|
|
fvarp->funcLocal(true);
|
|
|
|
|
fvarp->funcReturn(true);
|
|
|
|
|
fvarp->direction(VDirection::OUTPUT);
|
|
|
|
|
funcp = new AstFunc{nodep->fileline(), name, nullptr, fvarp};
|
|
|
|
|
funcp->dtypep(dtypep);
|
|
|
|
|
if (VN_IS(nodep, Class)) {
|
|
|
|
|
funcp->classMethod(true);
|
|
|
|
|
} else {
|
|
|
|
|
funcp->classMethod(false);
|
|
|
|
|
funcp->isStatic(true);
|
|
|
|
|
}
|
|
|
|
|
nodep->addStmtsp(funcp);
|
|
|
|
|
return funcp;
|
2023-04-01 16:50:27 +02:00
|
|
|
}
|
|
|
|
|
|
2024-07-15 16:42:41 +02:00
|
|
|
AstFunc* V3Randomize::newSRandomFunc(VMemberMap& memberMap, AstClass* nodep) {
|
2023-04-01 16:50:27 +02:00
|
|
|
AstClass* const basep = nodep->baseMostClassp();
|
2023-07-08 18:27:50 +02:00
|
|
|
AstFunc* funcp = VN_AS(memberMap.findMember(basep, "srandom"), Func);
|
2023-04-01 16:50:27 +02:00
|
|
|
if (!funcp) {
|
|
|
|
|
v3Global.useRandomizeMethods(true);
|
|
|
|
|
AstNodeDType* const dtypep
|
|
|
|
|
= basep->findBitDType(32, 32, VSigning::SIGNED); // IEEE says argument 0/1
|
|
|
|
|
AstVar* const ivarp = new AstVar{basep->fileline(), VVarType::MEMBER, "seed", dtypep};
|
2025-09-22 01:52:19 +02:00
|
|
|
ivarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
2023-04-01 16:50:27 +02:00
|
|
|
ivarp->funcLocal(true);
|
|
|
|
|
ivarp->direction(VDirection::INPUT);
|
|
|
|
|
funcp = new AstFunc{basep->fileline(), "srandom", ivarp, nullptr};
|
|
|
|
|
funcp->dtypep(basep->findVoidDType());
|
|
|
|
|
funcp->classMethod(true);
|
|
|
|
|
funcp->isVirtual(false);
|
|
|
|
|
basep->addMembersp(funcp);
|
2024-07-15 16:42:41 +02:00
|
|
|
memberMap.insert(nodep, funcp);
|
2025-09-26 14:25:47 +02:00
|
|
|
funcp->addStmtsp(new AstCStmt{basep->fileline(), "__Vm_rng.srandom(seed);"});
|
2023-04-01 16:50:27 +02:00
|
|
|
basep->needRNG(true);
|
2020-12-07 23:55:22 +01:00
|
|
|
}
|
|
|
|
|
return funcp;
|
|
|
|
|
}
|