parent
d0c4cc3938
commit
e0fdb933a0
|
|
@ -175,9 +175,14 @@ enum class VerilatedAssertDirectiveType : uint8_t {
|
|||
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
||||
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::type;
|
||||
|
||||
// Type trait for custom struct
|
||||
// Type trait: whether T is a user-defined custom struct
|
||||
template <typename>
|
||||
struct VlIsCustomStruct : public std::false_type {};
|
||||
|
||||
// Type trait: used to detect if array element is a custom struct (e.g. for struct arrays)
|
||||
template <typename T>
|
||||
struct VlContainsCustomStruct : VlIsCustomStruct<T> {};
|
||||
|
||||
//=============================================================================
|
||||
// Utility functions
|
||||
|
||||
|
|
|
|||
|
|
@ -200,8 +200,8 @@ class VlRandomizer final {
|
|||
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
||||
// variables
|
||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
||||
std::map<size_t, std::string> seen_values; // Record String Index to avoid conflicts
|
||||
const VlQueue<CData>* m_randmode; // rand_mode state;
|
||||
int m_index = 0; // Internal counter for key generation
|
||||
|
||||
// PRIVATE METHODS
|
||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||
|
|
@ -216,6 +216,11 @@ public:
|
|||
// Finds the next solution satisfying the constraints
|
||||
bool next(VlRNG& rngr);
|
||||
|
||||
// -----------------------------------------------
|
||||
// --- Process the key for associative array ---
|
||||
// -----------------------------------------------
|
||||
|
||||
// process_key: Handle integral keys (<= 32-bit)
|
||||
template <typename T_Key>
|
||||
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) <= 4)>::type
|
||||
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
|
||||
|
|
@ -225,6 +230,8 @@ public:
|
|||
= base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]";
|
||||
idx_width = sizeof(T_Key) * 8;
|
||||
}
|
||||
|
||||
// process_key: Handle integral keys (> 32-bit), split into 2 x 32-bit segments
|
||||
template <typename T_Key>
|
||||
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) > 4)>::type
|
||||
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
|
||||
|
|
@ -244,6 +251,8 @@ public:
|
|||
|
||||
idx_width = sizeof(T_Key) * 8;
|
||||
}
|
||||
|
||||
// process_key: Handle wide keys (VlWide-like), segment is 32-bit per element
|
||||
template <typename T_Key>
|
||||
typename std::enable_if<VlIsVlWide<T_Key>::value>::type
|
||||
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
|
||||
|
|
@ -261,6 +270,8 @@ public:
|
|||
indexed_name = base_name + "[" + index_string + "]";
|
||||
idx_width = key.size() * 32;
|
||||
}
|
||||
|
||||
// process_key: Handle string key, encoded as 128-bit hex
|
||||
template <typename T_Key>
|
||||
typename std::enable_if<std::is_same<T_Key, std::string>::value>::type
|
||||
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
|
||||
|
|
@ -289,6 +300,8 @@ public:
|
|||
|
||||
idx_width = 128;
|
||||
}
|
||||
|
||||
// process_key: Unsupported key type fallback
|
||||
template <typename T_Key>
|
||||
typename std::enable_if<!std::is_integral<T_Key>::value
|
||||
&& !std::is_same<T_Key, std::string>::value
|
||||
|
|
@ -300,8 +313,13 @@ public:
|
|||
"supported currently.");
|
||||
}
|
||||
|
||||
// -----------------------------------------
|
||||
// --- write_var to register variables ---
|
||||
// -----------------------------------------
|
||||
|
||||
// Register scalar variable (non-struct, basic type)
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlIsCustomStruct<T>::value, void>::type
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(T& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
|
|
@ -309,28 +327,60 @@ public:
|
|||
m_vars[name]
|
||||
= std::make_shared<const VlRandomVar>(name, width, &var, dimension, randmodeIdx);
|
||||
}
|
||||
|
||||
// Register user-defined struct variable by recursively writing members
|
||||
template <typename T>
|
||||
void write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
|
||||
write_var(T& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
modifyMembers(var, var.memberIndices(), name);
|
||||
}
|
||||
|
||||
// Register queue of non-struct types
|
||||
template <typename T>
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (dimension > 0) {
|
||||
idx = 0;
|
||||
m_index = 0;
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
}
|
||||
}
|
||||
|
||||
// Register queue of structs
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
||||
// Register unpacked array of non-struct types
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
typename std::enable_if<!VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (m_vars.find(name) != m_vars.end()) return;
|
||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (dimension > 0) {
|
||||
idx = 0;
|
||||
m_index = 0;
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
}
|
||||
}
|
||||
|
||||
// Register unpacked array of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
if (dimension > 0) record_struct_arr(var, name, dimension, {}, {});
|
||||
}
|
||||
|
||||
// Register associative array of non-struct types
|
||||
template <typename T_Key, typename T_Value>
|
||||
void write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
|
|
@ -339,59 +389,28 @@ public:
|
|||
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||
name, width, &var, dimension, randmodeIdx);
|
||||
if (dimension > 0) {
|
||||
idx = 0;
|
||||
m_index = 0;
|
||||
record_arr_table(var, name, dimension, {}, {});
|
||||
}
|
||||
}
|
||||
template <typename T, std::size_t... I>
|
||||
void modifyMembers(T& obj, std::index_sequence<I...>, std::string baseName) {
|
||||
// Use the indices to access each member via std::get
|
||||
(void)std::initializer_list<int>{
|
||||
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
|
||||
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
|
||||
0)...};
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
typename std::enable_if<VlIsCustomStruct<T>::value, void>::type
|
||||
write_var(T& var, int width, const char* name, int dimension,
|
||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||
modifyMembers(var, var.memberIndices(), name);
|
||||
}
|
||||
// TODO: Register associative array of structs
|
||||
|
||||
int idx;
|
||||
std::string generateKey(const std::string& name, int idx) {
|
||||
if (!name.empty() && name[0] == '\\') {
|
||||
const size_t space_pos = name.find(' ');
|
||||
return (space_pos != std::string::npos ? name.substr(0, space_pos) : name)
|
||||
+ std::to_string(idx);
|
||||
}
|
||||
const size_t bracket_pos = name.find('[');
|
||||
return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name)
|
||||
+ std::to_string(idx);
|
||||
}
|
||||
// ----------------------------------------
|
||||
// --- Record Arrays: flat and struct ---
|
||||
// ----------------------------------------
|
||||
|
||||
// Record a flat (non-class) element into the array variable table
|
||||
template <typename T>
|
||||
typename std::enable_if<!std::is_class<T>::value, void>::type
|
||||
record_arr_table(T& var, const std::string name, int dimension, std::vector<IData> indices,
|
||||
std::vector<size_t> idxWidths) {
|
||||
const std::string key = generateKey(name, idx);
|
||||
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices, idxWidths);
|
||||
++idx;
|
||||
}
|
||||
template <typename T>
|
||||
void record_arr_table(VlQueue<T>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
const std::string indexed_name = name + "[" + std::to_string(i) + "]";
|
||||
indices.push_back(i);
|
||||
record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
const std::string key = generateKey(name, m_index);
|
||||
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, m_index, indices, idxWidths);
|
||||
++m_index;
|
||||
}
|
||||
|
||||
// Recursively record all elements in an unpacked array
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
|
|
@ -406,6 +425,23 @@ public:
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in a queue
|
||||
template <typename T>
|
||||
void record_arr_table(VlQueue<T>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
const std::string indexed_name = name + "[" + std::to_string(i) + "]";
|
||||
indices.push_back(i);
|
||||
record_arr_table(var.atWrite(i), indexed_name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively record all elements in an associative array
|
||||
template <typename T_Key, typename T_Value>
|
||||
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
|
|
@ -433,6 +469,76 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
// Register a single structArray element via write_var
|
||||
template <typename T>
|
||||
typename std::enable_if<VlContainsCustomStruct<T>::value, void>::type
|
||||
record_struct_arr(T& var, const std::string name, int dimension, std::vector<IData> indices,
|
||||
std::vector<size_t> idxWidths) {
|
||||
std::ostringstream oss;
|
||||
for (size_t i = 0; i < indices.size(); ++i) {
|
||||
oss << std::hex << static_cast<int>(indices[i]);
|
||||
if (i < indices.size() - 1) oss << ".";
|
||||
}
|
||||
write_var(var, 1ULL, (name + "." + oss.str()).c_str(), 1ULL);
|
||||
}
|
||||
|
||||
// Recursively process VlUnpacked of structs
|
||||
template <typename T, std::size_t N_Depth>
|
||||
void record_struct_arr(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if (dimension > 0 && N_Depth != 0) {
|
||||
idxWidths.push_back(32);
|
||||
for (size_t i = 0; i < N_Depth; ++i) {
|
||||
indices.push_back(i);
|
||||
record_struct_arr(var.operator[](i), name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Recursively process VlQueue of structs
|
||||
template <typename T>
|
||||
void record_struct_arr(VlQueue<T>& var, const std::string name, int dimension,
|
||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
||||
if ((dimension > 0) && (var.size() != 0)) {
|
||||
idxWidths.push_back(32);
|
||||
for (size_t i = 0; i < var.size(); ++i) {
|
||||
indices.push_back(i);
|
||||
record_struct_arr(var.atWrite(i), name, dimension - 1, indices, idxWidths);
|
||||
indices.pop_back();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Add support for associative arrays of structs
|
||||
// Recursively process associative arrays of structs
|
||||
|
||||
// --------------------------
|
||||
// --- Helper functions ---
|
||||
// --------------------------
|
||||
|
||||
// Helper: Register all members of a user-defined struct
|
||||
template <typename T, std::size_t... I>
|
||||
void modifyMembers(T& obj, std::index_sequence<I...>, std::string baseName) {
|
||||
// Use the indices to access each member via std::get
|
||||
(void)std::initializer_list<int>{
|
||||
(write_var(std::get<I>(obj.getMembers(obj)), obj.memberWidth()[I],
|
||||
(baseName + "." + obj.memberNames()[I]).c_str(), obj.memberDimension()[I]),
|
||||
0)...};
|
||||
}
|
||||
|
||||
// Helper: Generate unique variable key from name and index
|
||||
std::string generateKey(const std::string& name, int idx) {
|
||||
if (!name.empty() && name[0] == '\\') {
|
||||
const size_t space_pos = name.find(' ');
|
||||
return (space_pos != std::string::npos ? name.substr(0, space_pos) : name)
|
||||
+ std::to_string(idx);
|
||||
}
|
||||
const size_t bracket_pos = name.find('[');
|
||||
return (bracket_pos != std::string::npos ? name.substr(0, bracket_pos) : name)
|
||||
+ std::to_string(idx);
|
||||
}
|
||||
|
||||
void hard(std::string&& constraint);
|
||||
void clear();
|
||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmode = &randmode; }
|
||||
|
|
|
|||
|
|
@ -926,6 +926,9 @@ std::string VL_TO_STRING(const VlQueue<T_Value, N_MaxSize>& obj) {
|
|||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <typename T_Value, size_t N_MaxSize>
|
||||
struct VlContainsCustomStruct<VlQueue<T_Value, N_MaxSize>> : VlContainsCustomStruct<T_Value> {};
|
||||
|
||||
//===================================================================
|
||||
// Verilog associative array container
|
||||
// There are no multithreaded locks on this; the base variable must
|
||||
|
|
@ -1261,6 +1264,9 @@ std::string VL_TO_STRING(const VlAssocArray<T_Key, T_Value>& obj) {
|
|||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
struct VlContainsCustomStruct<VlAssocArray<T_Key, T_Value>> : VlContainsCustomStruct<T_Key> {};
|
||||
|
||||
template <typename T_Key, typename T_Value>
|
||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
||||
VlAssocArray<T_Key, T_Value>& obj, QData start, QData end) VL_MT_SAFE {
|
||||
|
|
@ -1591,6 +1597,9 @@ std::string VL_TO_STRING(const VlUnpacked<T_Value, N_Depth>& obj) {
|
|||
return obj.to_string();
|
||||
}
|
||||
|
||||
template <typename T, int N>
|
||||
struct VlContainsCustomStruct<VlUnpacked<T, N>> : VlContainsCustomStruct<T> {};
|
||||
|
||||
//===================================================================
|
||||
// Helper to apply the given indices to a target expression
|
||||
|
||||
|
|
|
|||
|
|
@ -1927,6 +1927,7 @@ public:
|
|||
}
|
||||
ASTGEN_MEMBERS_AstSFormatF;
|
||||
string name() const override VL_MT_STABLE { return m_text; }
|
||||
void name(const string& name) override { m_text = name; }
|
||||
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||
bool sameNode(const AstNode* samep) const override {
|
||||
return text() == VN_DBG_AS(samep, SFormatF)->text();
|
||||
|
|
|
|||
|
|
@ -287,6 +287,7 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
||||
puts(";\n");
|
||||
}
|
||||
|
||||
// Three helper functions for struct constrained randomization:
|
||||
// - memberNames: Get member names
|
||||
// - getMembers: Access member references
|
||||
|
|
@ -294,13 +295,15 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
// - memberWidth: Retrieve member width
|
||||
// - memberDimension: Retrieve member dimension
|
||||
if (sdtypep->isConstrainedRand()) {
|
||||
bool needComma = false;
|
||||
putns(sdtypep, "\nstd::vector<std::string> memberNames(void) const {\n");
|
||||
puts("return {");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (itemp->isConstrainedRand()) putns(itemp, "\"" + itemp->shortName() + "\"");
|
||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
||||
puts(",\n");
|
||||
if (!itemp->isConstrainedRand()) continue;
|
||||
if (needComma) puts(",\n");
|
||||
putns(itemp, "\"" + itemp->shortName() + "\"");
|
||||
needComma = true;
|
||||
}
|
||||
puts("};\n}\n");
|
||||
|
||||
|
|
@ -310,25 +313,28 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
putns(sdtypep, "\nstd::vector<int> memberDimension(void) const {\n");
|
||||
emitMemberVector<AttributeType::Dimension>(sdtypep);
|
||||
|
||||
needComma = false;
|
||||
putns(sdtypep, "\nauto memberIndices(void) const {\n");
|
||||
puts("return std::index_sequence_for<");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (itemp->isConstrainedRand())
|
||||
putns(itemp, itemp->dtypep()->cType("", false, false));
|
||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
||||
puts(",\n");
|
||||
if (!itemp->isConstrainedRand()) continue;
|
||||
if (needComma) puts(",\n");
|
||||
putns(itemp, itemp->dtypep()->cType("", false, false));
|
||||
needComma = true;
|
||||
}
|
||||
puts(">{};\n}\n");
|
||||
|
||||
needComma = false;
|
||||
putns(sdtypep, "\ntemplate <typename T>");
|
||||
putns(sdtypep, "\nauto getMembers(T& obj) {\n");
|
||||
puts("return std::tie(");
|
||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||
if (itemp->isConstrainedRand()) putns(itemp, "obj." + itemp->nameProtect());
|
||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
||||
puts(", ");
|
||||
if (!itemp->isConstrainedRand()) continue;
|
||||
if (needComma) puts(",\n");
|
||||
putns(itemp, "obj." + itemp->nameProtect());
|
||||
needComma = true;
|
||||
}
|
||||
puts(");\n}\n");
|
||||
}
|
||||
|
|
|
|||
|
|
@ -481,6 +481,8 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstVar* m_randModeVarp; // Relevant randmode state variable
|
||||
bool m_wantSingle = false; // Whether to merge constraint expressions with LOGAND
|
||||
VMemberMap& m_memberMap; // Member names cached for fast lookup
|
||||
bool m_structSel = false; // Marks when inside structSel
|
||||
// (used to format "%@.%@" for struct arrays)
|
||||
|
||||
AstSFormatF* getConstFormat(AstNodeExpr* nodep) {
|
||||
return new AstSFormatF{nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false,
|
||||
|
|
@ -537,6 +539,10 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
UASSERT_OBJ(!rhsp, nodep, "Missing emitSMT %r for " << rhsp);
|
||||
UASSERT_OBJ(!thsp, nodep, "Missing emitSMT %t for " << thsp);
|
||||
AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp};
|
||||
if (m_structSel && newp->name() == "(select %@ %@)") {
|
||||
newp->name("%@.%@");
|
||||
newp->exprsp()->nextp()->name("%x");
|
||||
}
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
|
@ -712,6 +718,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
editSMT(nodep, nodep->fromp(), lsbp, msbp);
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
m_structSel = true;
|
||||
if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) {
|
||||
AstNodeExpr* const fromp = nodep->fromp();
|
||||
if (VN_IS(fromp, StructSel)) {
|
||||
|
|
@ -726,11 +733,34 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
memberp = VN_CAST(memberp->nextp(), MemberDType);
|
||||
}
|
||||
}
|
||||
// Mark Random for structArray
|
||||
if (VN_IS(nodep->fromp(), ArraySel)) {
|
||||
AstNodeExpr* const fromp = VN_AS(nodep->fromp(), ArraySel)->fromp();
|
||||
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);
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (editFormat(nodep)) return;
|
||||
FileLine* const fl = nodep->fileline();
|
||||
AstSFormatF* const newp
|
||||
= new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, nullptr};
|
||||
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)};
|
||||
newp->exprsp()->nextp()->name("%x");
|
||||
} else {
|
||||
newp = new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false,
|
||||
nullptr};
|
||||
}
|
||||
m_structSel = false;
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
|
|
@ -885,9 +915,14 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
|
||||
if (nodep->name() == "at" && nodep->fromp()->user1()) {
|
||||
iterateChildren(nodep);
|
||||
AstNodeExpr* const argsp
|
||||
= AstNode::addNext(nodep->fromp()->unlinkFrBack(), nodep->pinsp()->unlinkFrBack());
|
||||
AstSFormatF* const newp = new AstSFormatF{fl, "(select %@ %@)", false, argsp};
|
||||
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};
|
||||
nodep->replaceWith(newp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -112,15 +112,69 @@ class ArrayStruct;
|
|||
/* verilator lint_off SIDEEFFECT */
|
||||
endclass
|
||||
|
||||
class StructArray;
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
typedef struct {
|
||||
rand int arr[3];
|
||||
rand int a;
|
||||
rand bit [3:0] b;
|
||||
bit c;
|
||||
} struct_t;
|
||||
rand struct_t s_arr[2];
|
||||
|
||||
constraint c_structArray_0 {
|
||||
foreach (s_arr[i])
|
||||
foreach (s_arr[i].arr[j])
|
||||
s_arr[i].arr[j] inside {[0:9]};
|
||||
}
|
||||
constraint c_structArray_1 {
|
||||
foreach (s_arr[i]) s_arr[i].a inside {[10:20]};
|
||||
}
|
||||
|
||||
function new();
|
||||
foreach (s_arr[i]) begin
|
||||
foreach (s_arr[i].arr[j]) s_arr[i].arr[j] = 'h0 + j;
|
||||
s_arr[i].a = 'h10 + i;
|
||||
s_arr[i].b = 'h0 + i;
|
||||
s_arr[i].c = i;
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void print();
|
||||
foreach (s_arr[i]) begin
|
||||
foreach (s_arr[i].arr[j]) $display("s_arr[%0d].arr[%0d] = %0d", i, j, s_arr[i].arr[j]);
|
||||
$display("s_arr[%0d].a = %0d", i, s_arr[i].a);
|
||||
$display("s_arr[%0d].b = %0d", i, s_arr[i].b);
|
||||
$display("s_arr[%0d].c = %0d", i, s_arr[i].c);
|
||||
end
|
||||
endfunction
|
||||
|
||||
function void self_test();
|
||||
foreach (s_arr[i]) begin
|
||||
foreach (s_arr[i].arr[j]) if (!(s_arr[i].arr[j] inside {[0:9]})) $stop;
|
||||
if (!(s_arr[i].a inside {[10:20]})) $stop;
|
||||
if (!(s_arr[0].c == 0)) $stop;
|
||||
if (!(s_arr[1].c == 1)) $stop;
|
||||
end
|
||||
endfunction
|
||||
/* verilator lint_off WIDTHTRUNC */
|
||||
endclass
|
||||
|
||||
module t_constraint_struct_complex;
|
||||
int success;
|
||||
ArrayStruct asc;
|
||||
ArrayStruct as_c;
|
||||
StructArray sa_c;
|
||||
initial begin
|
||||
asc = new();
|
||||
success = asc.randomize();
|
||||
as_c = new();
|
||||
sa_c = new();
|
||||
success = as_c.randomize();
|
||||
if (success != 1) $stop;
|
||||
asc.self_test();
|
||||
// asc.print();
|
||||
as_c.self_test();
|
||||
// as_c.print();
|
||||
success = sa_c.randomize();
|
||||
if (success != 1) $stop;
|
||||
sa_c.self_test();
|
||||
// sa_c.print();
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
Loading…
Reference in New Issue