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-09-19 11:03:56 +02:00
IS_RANDOMIZED_WITH_GLOBAL_CONSTRAINTS , // 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
2025-09-19 11:03:56 +02:00
IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS , // randomize() with args and global constraints
2024-08-13 20:20:31 +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-09-19 11:03:56 +02:00
bool m_globalConsProcessed = false ;
AstWith * m_astWithp = nullptr ;
std : : vector < AstConstraint * > m_clonedConstraints ;
2025-09-19 16:11:58 +02:00
std : : unordered_set < const AstClass * > m_processedClasses ;
2025-09-19 11:03:56 +02:00
// Constants for global constraint processing
static constexpr const char * GLOBAL_CONSTRAINT_SEPARATOR = " __DT__ " ;
static constexpr const char * BASIC_RANDOMIZE_FUNC_NAME = " __Vbasic_randomize " ;
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
2025-09-19 11:03:56 +02:00
if ( ( nodep - > user1 ( ) = = IS_RANDOMIZED_INLINE )
| | ( nodep - > user1 ( ) = = IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS ) ) {
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-09-19 11:03:56 +02:00
void nameManipulation ( AstVarRef * fromp , AstConstraint * cloneCons ) {
2025-09-19 16:11:58 +02:00
if ( ! fromp | | ! cloneCons ) {
UINFO ( 9 , " Invalid parameters in nameManipulation \n " ) ;
return ;
}
2025-09-19 11:03:56 +02:00
// Iterate through all variable references and replace them with member selections.
cloneCons - > name ( fromp - > name ( ) + GLOBAL_CONSTRAINT_SEPARATOR + cloneCons - > name ( ) ) ;
cloneCons - > foreach ( [ & ] ( AstVarRef * varRefp ) {
2025-09-19 16:11:58 +02:00
if ( ! varRefp | | ! varRefp - > varp ( ) ) {
UINFO ( 9 , " Invalid variable reference in constraint \n " ) ;
return ;
}
AstVarRef * clonedFromp = fromp - > cloneTree ( false ) ;
if ( ! clonedFromp ) {
UINFO ( 9 , " Failed to clone variable reference in nameManipulation \n " ) ;
return ;
}
2025-09-19 16:13:39 +02:00
AstMemberSel * varMemberp
= 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 ) ;
} ) ;
}
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 ) ;
m_modp = m_classp = nodep ;
2025-09-19 11:03:56 +02:00
m_globalConsProcessed = false ;
2025-09-19 16:11:58 +02:00
// Clear processed classes for each new class context
m_processedClasses . clear ( ) ;
2023-03-18 17:17:25 +01:00
iterateChildrenConst ( nodep ) ;
2025-09-19 11:03:56 +02:00
for ( int i = 0 ; i < m_clonedConstraints . size ( ) ; i + + ) {
m_classp - > addStmtsp ( m_clonedConstraints [ i ] ) ;
}
m_clonedConstraints . clear ( ) ;
2020-12-07 23:55:22 +01:00
if ( nodep - > extendsp ( ) ) {
// Save pointer to derived class
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 ) ;
}
}
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 ) ) {
2024-09-02 15:45:47 +02:00
if ( methodHardp - > name ( ) = = " at " | | methodHardp - > name ( ) = = " atWrite " ) {
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-09-19 11:03:56 +02:00
if ( m_astWithp ) {
AstNode * backp = m_astWithp ;
while ( backp - > backp ( ) ) {
if ( VN_IS ( backp , MethodCall ) ) {
AstClassRefDType * classdtype = VN_CAST (
VN_AS ( backp , MethodCall ) - > fromp ( ) - > dtypep ( ) - > skipRefp ( ) , ClassRefDType ) ;
nodep - > user2p ( classdtype - > classp ( ) ) ;
break ;
}
backp = backp - > backp ( ) ;
}
} else
nodep - > user2p ( m_modp ) ;
if ( randObject
& & nodep - > varp ( ) - > rand ( ) . isRandomizable ( ) ) { // if global constraint is there
if ( m_classp - > user1 ( ) = = IS_RANDOMIZED )
m_classp - > user1 ( IS_RANDOMIZED_WITH_GLOBAL_CONSTRAINTS ) ;
else if ( m_classp - > user1 ( ) = = IS_RANDOMIZED_INLINE )
m_classp - > user1 ( IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS ) ;
2025-09-19 16:11:58 +02:00
// Mark the object variable as participating in global constraints
2025-09-28 11:10:36 +02:00
if ( VN_IS ( nodep - > fromp ( ) , VarRef ) ) VN_AS ( nodep - > fromp ( ) , VarRef ) - > varp ( ) - > setGlobalConstrained ( true ) ;
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 ) ;
if ( ! varRefp | | ! varRefp - > varp ( ) ) return ;
2025-09-19 16:13:39 +02:00
const AstClassRefDType * const classRefp
= VN_CAST ( varRefp - > dtypep ( ) - > skipRefp ( ) , ClassRefDType ) ;
2025-09-19 16:11:58 +02:00
if ( ! classRefp | | ! classRefp - > classp ( ) ) return ;
if ( nodep - > user1 ( ) & & varRefp - > varp ( ) - > isGlobalConstrained ( ) ) {
AstClass * gConsClass = classRefp - > classp ( ) ;
// Use class-specific processing to allow multiple object processing
if ( m_processedClasses . find ( gConsClass ) = = m_processedClasses . end ( ) ) {
2025-09-19 11:03:56 +02:00
2025-09-19 16:13:39 +02:00
gConsClass - > foreachMember ( [ & ] ( AstClass * const classp ,
AstConstraint * const constrp ) {
if ( ! constrp ) {
UINFO ( 9 , " Null constraint found in class " < < classp - > name ( ) < < " \n " ) ;
return ;
}
AstConstraint * cloneConstrp = constrp - > cloneTree ( false ) ;
if ( ! cloneConstrp ) {
UINFO ( 9 , " Failed to clone constraint " < < constrp - > name ( ) < < " \n " ) ;
return ;
}
// Name manipulation
nameManipulation ( varRefp , cloneConstrp ) ;
m_clonedConstraints . push_back ( cloneConstrp ) ;
} ) ;
2025-09-19 16:11:58 +02:00
// Mark this class as processed
m_processedClasses . insert ( gConsClass ) ;
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 {
m_astWithp = nodep ;
iterateChildrenConst ( nodep ) ;
m_astWithp = nullptr ;
}
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
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
= new AstBegin { fl , " [EditWrapper] " , itemsp - > unlinkFrBackWithNext ( ) } ;
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 ) )
selp = new AstCMethodHard { fl , arrayp , " at " , idxp } ;
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-09-19 11:03:56 +02:00
AstClass * withinclass = nullptr ;
2025-09-28 11:10:36 +02:00
AstNode * fromp = nodep - > backp ( ) ;
std : : string strr = nodep - > name ( ) + " . " + fromp - > name ( ) ;
while ( VN_IS ( fromp - > backp ( ) , MemberSel ) ) {
fromp = fromp - > backp ( ) ;
strr = strr + " . " + fromp - > name ( ) ;
}
AstMemberSel * membersel = VN_IS ( fromp , MemberSel )
? VN_AS ( fromp , MemberSel ) - > cloneTree ( false )
2025-07-25 12:13:46 +02:00
: nullptr ;
2025-09-28 11:10:36 +02:00
cout < < " IN ASTVARREF " < < " " < < strr < < endl ;
// AstMemberSel* membersel = VN_IS(nodep->backp(), MemberSel)
// ? VN_AS(nodep->backp(), MemberSel)->cloneTree(false)
// : nullptr;
2025-07-25 12:13:46 +02:00
if ( membersel ) varp = membersel - > varp ( ) ;
2025-09-28 11:10:36 +02:00
withinclass = membersel ? VN_CAST ( nodep - > backp ( ) - > user2p ( ) , Class ) : nullptr ;
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
// In SMT just variable name, but we also ensure write_var for the variable
2025-07-25 12:13:46 +02:00
const std : : string smtName = membersel
2025-09-28 11:10:36 +02:00
? strr //membersel->fromp()->name() + "." + membersel->name()
2025-07-25 12:13:46 +02:00
: nodep - > name ( ) ; // Can be anything unique
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 } ,
" at " , new AstConst { nodep - > fileline ( ) , randMode . index } } ;
atp - > dtypeSetUInt32 ( ) ;
exprp = new AstCond { varp - > fileline ( ) , atp , exprp , constFormatp } ;
} else {
2025-09-28 11:10:36 +02:00
//VL_DO_DANGLING(pushDeletep(nodep), nodep);
2024-07-31 23:30:48 +02:00
}
relinker . relink ( exprp ) ;
2024-05-17 16:38:34 +02:00
2025-09-19 11:03:56 +02:00
if ( ! varp - > user3 ( )
2025-09-28 11:10:36 +02:00
| | ( membersel & & nodep - > varp ( ) - > 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 } ,
2024-05-17 16:38:34 +02:00
" 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-09-19 11:03:56 +02:00
AstClass * const classp = membersel ? withinclass : VN_AS ( varp - > user2p ( ) , Class ) ;
2024-07-17 08:21:45 +02:00
AstVarRef * const varRefp
= new AstVarRef { varp - > fileline ( ) , classp , varp , VAccess : : WRITE } ;
2024-08-02 17:45:17 +02:00
varRefp - > classOrPackagep ( classOrPackagep ) ;
2025-07-25 12:13:46 +02:00
membersel ? methodp - > addPinsp ( membersel ) : 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-09-19 11:03:56 +02:00
// if (nodep->user1()) {
// nodep->v3warn(CONSTRAINTIGN, "Global constraints ignored (unsupported)");
// }
2025-09-28 11:10:36 +02:00
cout < < " HAHAHAHAHAHHAHA " < < endl ;
nodep - > dumpTreeJson ( cout ) ;
cout < < endl < < endl ;
2025-09-19 11:03:56 +02:00
if ( nodep - > varp ( ) - > rand ( ) . isRandomizable ( ) ) {
if ( nodep - > user2p ( )
2025-09-28 11:10:36 +02:00
= = VN_AS ( VN_IS ( nodep - > fromp ( ) , VarRef ) ? VN_AS ( nodep - > fromp ( ) , VarRef ) - > dtypep ( ) : VN_AS ( nodep - > fromp ( ) , MemberSel ) - > dtypep ( ) , ClassRefDType )
2025-09-19 11:03:56 +02:00
- > classp ( ) ) // contraint is from inside the class or outside( with class
// var or outside var)
{
// fromp == constrained contained class
iterateChildren ( nodep ) ;
nodep - > replaceWith ( nodep - > fromp ( ) - > unlinkFrBack ( ) ) ;
VL_DO_DANGLING ( nodep - > deleteTree ( ) , nodep ) ;
return ;
}
2025-09-28 11:10:36 +02:00
AstNode * fromp = nodep - > fromp ( ) ;
while ( ! VN_IS ( fromp , NodeVarRef ) ) {
fromp = VN_AS ( fromp , MemberSel ) - > fromp ( ) ;
}
if ( VN_IS ( nodep - > fromp ( ) , VarRef ) ? VN_AS ( nodep - > fromp ( ) , VarRef ) - > varp ( ) - > isGlobalConstrained ( ) : VN_AS ( nodep - > fromp ( ) , MemberSel ) - > varp ( ) - > isGlobalConstrained ( ) ) { // if (VN_AS(nodep->fromp(), VarRef)->varp()->isGlobalConstrained()) {
2025-09-19 11:03:56 +02:00
iterateChildren ( nodep ) ;
nodep - > replaceWith ( nodep - > fromp ( ) - > unlinkFrBack ( ) ) ;
VL_DO_DANGLING ( nodep - > deleteTree ( ) , nodep ) ;
return ;
}
2024-11-22 14:47:14 +01:00
}
editFormat ( nodep ) ;
}
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 ) ;
}
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 ( ) ;
if ( m_wantSingle ) {
AstNode * const itemp = editSingle ( fl , nodep - > stmtsp ( ) ) ;
AstNode * const cstmtp = new AstText { fl , " ret += \" \" + " } ;
cstmtp - > addNext ( itemp ) ;
cstmtp - > addNext ( new AstText { fl , " ; " } ) ;
2024-08-27 15:39:36 +02:00
AstNode * const exprsp = new AstText { fl , " ([&]{ std::string ret; " } ;
2024-08-02 16:03:55 +02:00
exprsp - > addNext ( new AstBegin {
fl , " " ,
new AstForeach { fl , nodep - > arrayp ( ) - > unlinkFrBack ( ) , new AstCStmt { fl , cstmtp } } ,
false , true } ) ;
2024-08-27 15:39:36 +02:00
exprsp - > addNext (
2024-09-19 13:27:59 +02:00
new AstText { fl , " return ret.empty() ? \" #b1 \" : \" (bvand \" + ret + \" ) \" ;})() " } ) ;
2024-08-02 16:03:55 +02:00
AstNodeExpr * const newp = new AstCExpr { fl , exprsp } ;
newp - > dtypeSetString ( ) ;
nodep - > replaceWith ( new AstSFormatF { fl , " %@ " , false , newp } ) ;
} else {
iterateAndNextNull ( nodep - > stmtsp ( ) ) ;
2024-09-10 13:17:21 +02:00
nodep - > replaceWith (
new AstBegin { fl , " " ,
new AstForeach { fl , nodep - > arrayp ( ) - > unlinkFrBack ( ) ,
nodep - > stmtsp ( ) - > unlinkFrBackWithNext ( ) } ,
false , 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 } ,
2024-07-10 17:30:18 +02:00
" hard " , nodep - > exprp ( ) - > unlinkFrBack ( ) } ;
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
2024-09-10 15:33:14 +02:00
if ( nodep - > name ( ) = = " at " & & nodep - > fromp ( ) - > user1 ( ) ) {
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
2024-09-19 13:27:59 +02:00
if ( nodep - > name ( ) = = " inside " ) {
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 ) ;
AstNode * const cstmtp = new AstText { fl , " ret += \" \" + " } ;
cstmtp - > addNext ( iterateSubtreeReturnEdits ( itemp ) ) ;
cstmtp - > addNext ( new AstText { fl , " ; " } ) ;
AstNode * const exprsp = new AstText { fl , " ([&]{ std::string ret; " } ;
exprsp - > addNext ( new AstBegin {
fl , " " , new AstForeach { fl , arrayp , new AstCStmt { fl , cstmtp } } , false , true } ) ;
exprsp - > addNext (
new AstText { fl , " return ret.empty() ? \" #b0 \" : \" (bvor \" + ret + \" ) \" ;})() " } ) ;
AstNodeExpr * const newp = new AstCExpr { fl , exprsp } ;
newp - > dtypeSetString ( ) ;
nodep - > replaceWith ( new AstSFormatF { fl , " %@ " , false , newp } ) ;
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 ) ;
newVarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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 ) ;
m_thisp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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-09-19 11:03:56 +02:00
= new AstMemberSel ( nodep - > fileline ( ) , thisRefp , nodep - > varp ( ) ) ;
thisRefp - > user1 ( true ) ;
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
2025-09-19 11:03:56 +02:00
std : : map < AstClass * , AstStmtExpr * >
m_clonedConstraints ; // Map of the class to the cloned constraint from the instantiated
// object
// Constants for global constraint processing
static constexpr const char * BASIC_RANDOMIZE_FUNC_NAME = " __Vbasic_randomize " ;
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 } ,
" set_randmode " ,
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 } ,
2024-11-15 16:45:06 +01:00
" 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 ) ;
iterVarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
AstCMethodHard * const sizep = new AstCMethodHard { fl , lhsp , " size " , nullptr } ;
sizep - > dtypeSetUInt32 ( ) ;
2024-09-02 15:45:47 +02:00
AstCMethodHard * const setp = new AstCMethodHard {
fl , lhsp - > cloneTree ( false ) , " atWrite " , new AstVarRef { fl , iterVarp , VAccess : : READ } } ;
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 } } ) ;
stmtsp - > addNext (
new AstWhile { fl , new AstLt { fl , new AstVarRef { fl , iterVarp , VAccess : : READ } , sizep } ,
2024-09-02 15:45:47 +02:00
new AstAssign { fl , setp , rhsp } ,
2024-07-31 23:30:48 +02:00
new AstAssign { fl , new AstVarRef { fl , iterVarp , VAccess : : WRITE } ,
new AstAdd { fl , new AstConst { fl , 1 } ,
new AstVarRef { fl , iterVarp , VAccess : : READ } } } } ) ;
return new AstBegin { fl , " " , stmtsp , false , true } ;
}
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 } ,
" 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 ) ) {
2024-10-08 16:54:20 +02:00
tempElementp = new AstCMethodHard { fl , tempExprp , " atWrite " , 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 ) ) {
2024-10-08 16:54:20 +02:00
tempElementp = new AstCMethodHard { fl , tempExprp , " atWriteAppend " , 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 ) {
2024-07-12 16:18:18 +02:00
AstVarRef * const argsp = new AstVarRef { fl , randcVarp , VAccess : : READWRITE } ;
argsp - > AstNode : : addNext ( new AstText { fl , " .randomize(__Vm_rng) " } ) ;
AstCExpr * const newp = new AstCExpr { fl , argsp } ;
2023-09-19 03:17:21 +02:00
newp - > dtypep ( dtypep ) ;
return newp ;
} else {
return new AstRandRNG { fl , dtypep } ;
}
}
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 } ,
" 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 ) ;
randModeTmpVarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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 } ,
2024-09-02 15:45:47 +02:00
" atWrite " , new AstConst { nodep - > fileline ( ) , randMode . index } } ;
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-09-19 11:03:56 +02:00
= memberVarp - > isGlobalConstrained ( )
? 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-09-19 11:03:56 +02:00
= memberVarp - > isGlobalConstrained ( )
? 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 ) ;
2024-10-08 16:54:20 +02:00
basicRandomizep - > addStmtsp ( new AstBegin { fl , " " , stmtp } ) ;
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 " ) ;
2024-09-02 15:45:47 +02:00
AstCMethodHard * const setp
= new AstCMethodHard { fl , lhsp , " atWrite " , new AstConst { fl , rmode . index } } ;
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 " ) ;
2024-09-02 15:45:47 +02:00
AstCMethodHard * const setp
= new AstCMethodHard { fl , lhsp , " atWrite " , new AstConst { fl , rmode . index } } ;
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 ( ) } ;
2024-09-02 15:45:47 +02:00
AstCMethodHard * setp
2024-08-13 20:20:31 +02:00
= new AstCMethodHard { fl , makeSiblingRefp ( exprp , randModeVarp , VAccess : : WRITE ) ,
2024-09-02 15:45:47 +02:00
" atWrite " , new AstConst { fl , randMode . index } } ;
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 ) ;
relinker . relink ( new AstBegin { nodep - > fileline ( ) , " " , stmtsp , false , true } ) ;
}
}
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
bool globalcons = nodep - > user1 ( ) = = IS_RANDOMIZED_INLINE_WITH_GLOBAL_CONSTRAINTS
| | nodep - > user1 ( ) = = IS_RANDOMIZED_WITH_GLOBAL_CONSTRAINTS ;
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 ) ;
2025-09-19 11:03:56 +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 } ;
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 ) ;
AstVarRef * const genRefp = new AstVarRef { fl , genModp , genp , VAccess : : READWRITE } ;
2024-07-17 08:21:45 +02:00
AstNode * const argsp = genRefp ;
argsp - > addNext ( new AstText { fl , " .next(__Vm_rng) " } ) ;
2024-07-31 23:30:48 +02:00
2024-07-17 08:21:45 +02:00
AstNodeExpr * const solverCallp = new AstCExpr { fl , argsp } ;
2024-05-17 16:38:34 +02:00
solverCallp - > dtypeSetBit ( ) ;
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-09-19 11:03:56 +02:00
randomizep - > addStmtsp (
new AstAssign { fl , fvarRefp , globalcons ? basicRandomizeCallp : 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-09-19 11:03:56 +02:00
randomizep - > addStmtsp ( new AstAssign {
fl , fvarRefp - > cloneTree ( false ) ,
new AstAnd { fl , fvarRefReadp , globalcons ? beginValp : basicRandomizeCallp } } ) ;
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 } ,
" basicStdRandomization " } ;
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 ) ;
refvarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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
AstVarRef * const randNextp
= new AstVarRef { nodep - > fileline ( ) , localGenp , VAccess : : READWRITE } ;
randNextp - > AstNode : : addNext ( new AstText { nodep - > fileline ( ) , " .next(__Vm_rng) " } ) ;
AstNodeExpr * const solverCallp = new AstCExpr { nodep - > fileline ( ) , randNextp } ;
solverCallp - > dtypeSetBit ( ) ;
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 ( ) ;
if ( m_constraintp & & nodep - > fromp ( ) - > user1 ( ) & & nodep - > name ( ) = = " size " ) {
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
= new AstCMethodHard { fl , nodep - > fromp ( ) - > unlinkFrBack ( ) , " resize " ,
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 } ;
2020-12-07 23:55:22 +01:00
fvarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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 } ;
fvarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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 } ;
ivarp - > lifetime ( VLifetime : : AUTOMATIC ) ;
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 ) ;
2023-04-01 16:50:27 +02:00
funcp - > addStmtsp ( new AstCStmt { basep - > fileline ( ) , " __Vm_rng.srandom(seed); \n " } ) ;
basep - > needRNG ( true ) ;
2020-12-07 23:55:22 +01:00
}
return funcp ;
}