parent
d0c4cc3938
commit
e0fdb933a0
|
|
@ -175,9 +175,14 @@ enum class VerilatedAssertDirectiveType : uint8_t {
|
||||||
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
using VerilatedAssertType_t = std::underlying_type<VerilatedAssertType>::type;
|
||||||
using VerilatedAssertDirectiveType_t = std::underlying_type<VerilatedAssertDirectiveType>::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>
|
template <typename>
|
||||||
struct VlIsCustomStruct : public std::false_type {};
|
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
|
// Utility functions
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -200,8 +200,8 @@ class VlRandomizer final {
|
||||||
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
std::map<std::string, std::shared_ptr<const VlRandomVar>> m_vars; // Solver-dependent
|
||||||
// variables
|
// variables
|
||||||
ArrayInfoMap m_arr_vars; // Tracks each element in array structures for iteration
|
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;
|
const VlQueue<CData>* m_randmode; // rand_mode state;
|
||||||
|
int m_index = 0; // Internal counter for key generation
|
||||||
|
|
||||||
// PRIVATE METHODS
|
// PRIVATE METHODS
|
||||||
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
void randomConstraint(std::ostream& os, VlRNG& rngr, int bits);
|
||||||
|
|
@ -216,6 +216,11 @@ public:
|
||||||
// Finds the next solution satisfying the constraints
|
// Finds the next solution satisfying the constraints
|
||||||
bool next(VlRNG& rngr);
|
bool next(VlRNG& rngr);
|
||||||
|
|
||||||
|
// -----------------------------------------------
|
||||||
|
// --- Process the key for associative array ---
|
||||||
|
// -----------------------------------------------
|
||||||
|
|
||||||
|
// process_key: Handle integral keys (<= 32-bit)
|
||||||
template <typename T_Key>
|
template <typename T_Key>
|
||||||
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) <= 4)>::type
|
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,
|
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]) + "]";
|
= base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]";
|
||||||
idx_width = sizeof(T_Key) * 8;
|
idx_width = sizeof(T_Key) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process_key: Handle integral keys (> 32-bit), split into 2 x 32-bit segments
|
||||||
template <typename T_Key>
|
template <typename T_Key>
|
||||||
typename std::enable_if<std::is_integral<T_Key>::value && (sizeof(T_Key) > 4)>::type
|
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,
|
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;
|
idx_width = sizeof(T_Key) * 8;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process_key: Handle wide keys (VlWide-like), segment is 32-bit per element
|
||||||
template <typename T_Key>
|
template <typename T_Key>
|
||||||
typename std::enable_if<VlIsVlWide<T_Key>::value>::type
|
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,
|
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 + "]";
|
indexed_name = base_name + "[" + index_string + "]";
|
||||||
idx_width = key.size() * 32;
|
idx_width = key.size() * 32;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process_key: Handle string key, encoded as 128-bit hex
|
||||||
template <typename T_Key>
|
template <typename T_Key>
|
||||||
typename std::enable_if<std::is_same<T_Key, std::string>::value>::type
|
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,
|
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
|
||||||
|
|
@ -289,6 +300,8 @@ public:
|
||||||
|
|
||||||
idx_width = 128;
|
idx_width = 128;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// process_key: Unsupported key type fallback
|
||||||
template <typename T_Key>
|
template <typename T_Key>
|
||||||
typename std::enable_if<!std::is_integral<T_Key>::value
|
typename std::enable_if<!std::is_integral<T_Key>::value
|
||||||
&& !std::is_same<T_Key, std::string>::value
|
&& !std::is_same<T_Key, std::string>::value
|
||||||
|
|
@ -300,8 +313,13 @@ public:
|
||||||
"supported currently.");
|
"supported currently.");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// -----------------------------------------
|
||||||
|
// --- write_var to register variables ---
|
||||||
|
// -----------------------------------------
|
||||||
|
|
||||||
|
// Register scalar variable (non-struct, basic type)
|
||||||
template <typename T>
|
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,
|
write_var(T& var, int width, const char* name, int dimension,
|
||||||
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (m_vars.find(name) != m_vars.end()) return;
|
if (m_vars.find(name) != m_vars.end()) return;
|
||||||
|
|
@ -309,28 +327,60 @@ public:
|
||||||
m_vars[name]
|
m_vars[name]
|
||||||
= std::make_shared<const VlRandomVar>(name, width, &var, dimension, randmodeIdx);
|
= std::make_shared<const VlRandomVar>(name, width, &var, dimension, randmodeIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Register user-defined struct variable by recursively writing members
|
||||||
template <typename T>
|
template <typename T>
|
||||||
void write_var(VlQueue<T>& var, int width, const char* name, int dimension,
|
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()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (m_vars.find(name) != m_vars.end()) return;
|
if (m_vars.find(name) != m_vars.end()) return;
|
||||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlQueue<T>>>(
|
||||||
name, width, &var, dimension, randmodeIdx);
|
name, width, &var, dimension, randmodeIdx);
|
||||||
if (dimension > 0) {
|
if (dimension > 0) {
|
||||||
idx = 0;
|
m_index = 0;
|
||||||
record_arr_table(var, name, dimension, {}, {});
|
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>
|
template <typename T, std::size_t N_Depth>
|
||||||
void write_var(VlUnpacked<T, N_Depth>& var, int width, const char* name, int dimension,
|
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()) {
|
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
|
||||||
if (m_vars.find(name) != m_vars.end()) return;
|
if (m_vars.find(name) != m_vars.end()) return;
|
||||||
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
m_vars[name] = std::make_shared<const VlRandomArrayVarTemplate<VlUnpacked<T, N_Depth>>>(
|
||||||
name, width, &var, dimension, randmodeIdx);
|
name, width, &var, dimension, randmodeIdx);
|
||||||
if (dimension > 0) {
|
if (dimension > 0) {
|
||||||
idx = 0;
|
m_index = 0;
|
||||||
record_arr_table(var, name, dimension, {}, {});
|
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>
|
template <typename T_Key, typename T_Value>
|
||||||
void write_var(VlAssocArray<T_Key, T_Value>& var, int width, const char* name, int dimension,
|
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()) {
|
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>>>(
|
= std::make_shared<const VlRandomArrayVarTemplate<VlAssocArray<T_Key, T_Value>>>(
|
||||||
name, width, &var, dimension, randmodeIdx);
|
name, width, &var, dimension, randmodeIdx);
|
||||||
if (dimension > 0) {
|
if (dimension > 0) {
|
||||||
idx = 0;
|
m_index = 0;
|
||||||
record_arr_table(var, name, dimension, {}, {});
|
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>
|
// TODO: Register associative array of structs
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
int idx;
|
// ----------------------------------------
|
||||||
std::string generateKey(const std::string& name, int idx) {
|
// --- Record Arrays: flat and struct ---
|
||||||
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 a flat (non-class) element into the array variable table
|
||||||
template <typename T>
|
template <typename T>
|
||||||
typename std::enable_if<!std::is_class<T>::value, void>::type
|
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,
|
record_arr_table(T& var, const std::string name, int dimension, std::vector<IData> indices,
|
||||||
std::vector<size_t> idxWidths) {
|
std::vector<size_t> idxWidths) {
|
||||||
const std::string key = generateKey(name, idx);
|
const std::string key = generateKey(name, m_index);
|
||||||
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, idx, indices, idxWidths);
|
m_arr_vars[key] = std::make_shared<ArrayInfo>(name, &var, m_index, indices, idxWidths);
|
||||||
++idx;
|
++m_index;
|
||||||
}
|
|
||||||
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 unpacked array
|
||||||
template <typename T, std::size_t N_Depth>
|
template <typename T, std::size_t N_Depth>
|
||||||
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
|
void record_arr_table(VlUnpacked<T, N_Depth>& var, const std::string name, int dimension,
|
||||||
std::vector<IData> indices, std::vector<size_t> idxWidths) {
|
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>
|
template <typename T_Key, typename T_Value>
|
||||||
void record_arr_table(VlAssocArray<T_Key, T_Value>& var, const std::string name, int dimension,
|
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) {
|
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 hard(std::string&& constraint);
|
||||||
void clear();
|
void clear();
|
||||||
void set_randmode(const VlQueue<CData>& randmode) { m_randmode = &randmode; }
|
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();
|
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
|
// Verilog associative array container
|
||||||
// There are no multithreaded locks on this; the base variable must
|
// 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();
|
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>
|
template <typename T_Key, typename T_Value>
|
||||||
void VL_READMEM_N(bool hex, int bits, const std::string& filename,
|
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 {
|
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();
|
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
|
// Helper to apply the given indices to a target expression
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1927,6 +1927,7 @@ public:
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstSFormatF;
|
ASTGEN_MEMBERS_AstSFormatF;
|
||||||
string name() const override VL_MT_STABLE { return m_text; }
|
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; }
|
int instrCount() const override { return INSTR_COUNT_PLI; }
|
||||||
bool sameNode(const AstNode* samep) const override {
|
bool sameNode(const AstNode* samep) const override {
|
||||||
return text() == VN_DBG_AS(samep, SFormatF)->text();
|
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));
|
putns(itemp, itemp->dtypep()->cType(itemp->nameProtect(), false, false));
|
||||||
puts(";\n");
|
puts(";\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Three helper functions for struct constrained randomization:
|
// Three helper functions for struct constrained randomization:
|
||||||
// - memberNames: Get member names
|
// - memberNames: Get member names
|
||||||
// - getMembers: Access member references
|
// - getMembers: Access member references
|
||||||
|
|
@ -294,13 +295,15 @@ class EmitCHeader final : public EmitCConstInit {
|
||||||
// - memberWidth: Retrieve member width
|
// - memberWidth: Retrieve member width
|
||||||
// - memberDimension: Retrieve member dimension
|
// - memberDimension: Retrieve member dimension
|
||||||
if (sdtypep->isConstrainedRand()) {
|
if (sdtypep->isConstrainedRand()) {
|
||||||
|
bool needComma = false;
|
||||||
putns(sdtypep, "\nstd::vector<std::string> memberNames(void) const {\n");
|
putns(sdtypep, "\nstd::vector<std::string> memberNames(void) const {\n");
|
||||||
puts("return {");
|
puts("return {");
|
||||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
if (itemp->isConstrainedRand()) putns(itemp, "\"" + itemp->shortName() + "\"");
|
if (!itemp->isConstrainedRand()) continue;
|
||||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
if (needComma) puts(",\n");
|
||||||
puts(",\n");
|
putns(itemp, "\"" + itemp->shortName() + "\"");
|
||||||
|
needComma = true;
|
||||||
}
|
}
|
||||||
puts("};\n}\n");
|
puts("};\n}\n");
|
||||||
|
|
||||||
|
|
@ -310,25 +313,28 @@ class EmitCHeader final : public EmitCConstInit {
|
||||||
putns(sdtypep, "\nstd::vector<int> memberDimension(void) const {\n");
|
putns(sdtypep, "\nstd::vector<int> memberDimension(void) const {\n");
|
||||||
emitMemberVector<AttributeType::Dimension>(sdtypep);
|
emitMemberVector<AttributeType::Dimension>(sdtypep);
|
||||||
|
|
||||||
|
needComma = false;
|
||||||
putns(sdtypep, "\nauto memberIndices(void) const {\n");
|
putns(sdtypep, "\nauto memberIndices(void) const {\n");
|
||||||
puts("return std::index_sequence_for<");
|
puts("return std::index_sequence_for<");
|
||||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
if (itemp->isConstrainedRand())
|
if (!itemp->isConstrainedRand()) continue;
|
||||||
|
if (needComma) puts(",\n");
|
||||||
putns(itemp, itemp->dtypep()->cType("", false, false));
|
putns(itemp, itemp->dtypep()->cType("", false, false));
|
||||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
needComma = true;
|
||||||
puts(",\n");
|
|
||||||
}
|
}
|
||||||
puts(">{};\n}\n");
|
puts(">{};\n}\n");
|
||||||
|
|
||||||
|
needComma = false;
|
||||||
putns(sdtypep, "\ntemplate <typename T>");
|
putns(sdtypep, "\ntemplate <typename T>");
|
||||||
putns(sdtypep, "\nauto getMembers(T& obj) {\n");
|
putns(sdtypep, "\nauto getMembers(T& obj) {\n");
|
||||||
puts("return std::tie(");
|
puts("return std::tie(");
|
||||||
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
for (const AstMemberDType* itemp = sdtypep->membersp(); itemp;
|
||||||
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
itemp = VN_AS(itemp->nextp(), MemberDType)) {
|
||||||
if (itemp->isConstrainedRand()) putns(itemp, "obj." + itemp->nameProtect());
|
if (!itemp->isConstrainedRand()) continue;
|
||||||
if (itemp->nextp() && VN_AS(itemp->nextp(), MemberDType)->isConstrainedRand())
|
if (needComma) puts(",\n");
|
||||||
puts(", ");
|
putns(itemp, "obj." + itemp->nameProtect());
|
||||||
|
needComma = true;
|
||||||
}
|
}
|
||||||
puts(");\n}\n");
|
puts(");\n}\n");
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -481,6 +481,8 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
AstVar* m_randModeVarp; // Relevant randmode state variable
|
AstVar* m_randModeVarp; // Relevant randmode state variable
|
||||||
bool m_wantSingle = false; // Whether to merge constraint expressions with LOGAND
|
bool m_wantSingle = false; // Whether to merge constraint expressions with LOGAND
|
||||||
VMemberMap& m_memberMap; // Member names cached for fast lookup
|
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) {
|
AstSFormatF* getConstFormat(AstNodeExpr* nodep) {
|
||||||
return new AstSFormatF{nodep->fileline(), (nodep->width() & 3) ? "#b%b" : "#x%x", false,
|
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(!rhsp, nodep, "Missing emitSMT %r for " << rhsp);
|
||||||
UASSERT_OBJ(!thsp, nodep, "Missing emitSMT %t for " << thsp);
|
UASSERT_OBJ(!thsp, nodep, "Missing emitSMT %t for " << thsp);
|
||||||
AstSFormatF* const newp = new AstSFormatF{nodep->fileline(), smtExpr, false, argsp};
|
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);
|
nodep->replaceWith(newp);
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -712,6 +718,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
editSMT(nodep, nodep->fromp(), lsbp, msbp);
|
editSMT(nodep, nodep->fromp(), lsbp, msbp);
|
||||||
}
|
}
|
||||||
void visit(AstStructSel* nodep) override {
|
void visit(AstStructSel* nodep) override {
|
||||||
|
m_structSel = true;
|
||||||
if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) {
|
if (VN_IS(nodep->fromp()->dtypep()->skipRefp(), StructDType)) {
|
||||||
AstNodeExpr* const fromp = nodep->fromp();
|
AstNodeExpr* const fromp = nodep->fromp();
|
||||||
if (VN_IS(fromp, StructSel)) {
|
if (VN_IS(fromp, StructSel)) {
|
||||||
|
|
@ -726,11 +733,34 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
memberp = VN_CAST(memberp->nextp(), MemberDType);
|
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);
|
iterateChildren(nodep);
|
||||||
if (editFormat(nodep)) return;
|
if (editFormat(nodep)) return;
|
||||||
FileLine* const fl = nodep->fileline();
|
FileLine* const fl = nodep->fileline();
|
||||||
AstSFormatF* const newp
|
AstSFormatF* newp = nullptr;
|
||||||
= new AstSFormatF{fl, nodep->fromp()->name() + "." + nodep->name(), false, 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);
|
nodep->replaceWith(newp);
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
}
|
}
|
||||||
|
|
@ -885,9 +915,14 @@ class ConstraintExprVisitor final : public VNVisitor {
|
||||||
|
|
||||||
if (nodep->name() == "at" && nodep->fromp()->user1()) {
|
if (nodep->name() == "at" && nodep->fromp()->user1()) {
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
AstNodeExpr* const argsp
|
AstNodeExpr* pinp = nodep->pinsp()->unlinkFrBack();
|
||||||
= AstNode::addNext(nodep->fromp()->unlinkFrBack(), nodep->pinsp()->unlinkFrBack());
|
if (VN_IS(pinp, SFormatF) && m_structSel) VN_AS(pinp, SFormatF)->name("%x");
|
||||||
AstSFormatF* const newp = new AstSFormatF{fl, "(select %@ %@)", false, argsp};
|
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);
|
nodep->replaceWith(newp);
|
||||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
return;
|
return;
|
||||||
|
|
|
||||||
|
|
@ -112,15 +112,69 @@ class ArrayStruct;
|
||||||
/* verilator lint_off SIDEEFFECT */
|
/* verilator lint_off SIDEEFFECT */
|
||||||
endclass
|
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;
|
module t_constraint_struct_complex;
|
||||||
int success;
|
int success;
|
||||||
ArrayStruct asc;
|
ArrayStruct as_c;
|
||||||
|
StructArray sa_c;
|
||||||
initial begin
|
initial begin
|
||||||
asc = new();
|
as_c = new();
|
||||||
success = asc.randomize();
|
sa_c = new();
|
||||||
|
success = as_c.randomize();
|
||||||
if (success != 1) $stop;
|
if (success != 1) $stop;
|
||||||
asc.self_test();
|
as_c.self_test();
|
||||||
// asc.print();
|
// as_c.print();
|
||||||
|
success = sa_c.randomize();
|
||||||
|
if (success != 1) $stop;
|
||||||
|
sa_c.self_test();
|
||||||
|
// sa_c.print();
|
||||||
$write("*-* All Finished *-*\n");
|
$write("*-* All Finished *-*\n");
|
||||||
$finish;
|
$finish;
|
||||||
end
|
end
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue