Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[pull] master from verilator:master #626

Merged
merged 2 commits into from
Jan 9, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 12 additions & 2 deletions include/verilated_random.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,7 @@ std::string parseNestedSelect(const std::string& nested_select_expr,
indices.push_back(idx);
return name;
}

//======================================================================
// VlRandomizer:: Methods

Expand Down Expand Up @@ -467,10 +468,19 @@ bool VlRandomizer::parseSolution(std::iostream& f) {
"hex_index contains invalid format");
continue;
}
const long long index = std::stoll(hex_index.substr(start + 2), nullptr, 16);
oss << "[" << index << "]";
std::string trimmed_hex = hex_index.substr(start + 2);

if (trimmed_hex.size() <= 8) { // Small numbers: <= 32 bits
// Convert to decimal and output directly
oss << "[" << std::to_string(std::stoll(trimmed_hex, nullptr, 16)) << "]";
} else { // Large numbers: > 32 bits
// Trim leading zeros and handle empty case
trimmed_hex.erase(0, trimmed_hex.find_first_not_of('0'));
oss << "[" << (trimmed_hex.empty() ? "0" : trimmed_hex) << "]";
}
}
const std::string indexed_name = oss.str();

const auto it = std::find_if(m_arr_vars.begin(), m_arr_vars.end(),
[&indexed_name](const auto& entry) {
return entry.second->m_name == indexed_name;
Expand Down
153 changes: 110 additions & 43 deletions include/verilated_random.h
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@

#include "verilated.h"

#include <iomanip>
#include <iostream>
#include <ostream>
#include <sstream>

//=============================================================================
// VlRandomExpr and subclasses represent expressions for the constraint solver.
Expand All @@ -38,10 +40,10 @@ class ArrayInfo final {
m_name; // Name of the array variable, including index notation (e.g., arr[2][1])
void* const m_datap; // Reference to the array variable data
const int m_index; // Flattened (1D) index of the array element
const std::vector<size_t> m_indices; // Multi-dimensional indices of the array element
const std::vector<IData> m_indices; // Multi-dimensional indices of the array element
const std::vector<size_t> m_idxWidths; // Multi-dimensional indices' bit widths

ArrayInfo(const std::string& name, void* datap, int index, const std::vector<size_t>& indices,
ArrayInfo(const std::string& name, void* datap, int index, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths)
: m_name(name)
, m_datap(datap)
Expand Down Expand Up @@ -110,16 +112,31 @@ class VlRandomArrayVarTemplate final : public VlRandomVar {
return nullptr;
}
}
void emitSelect(std::ostream& s, const std::vector<size_t>& indices,
void emitHexs(std::ostream& s, const std::vector<IData>& indices, const size_t bit_width,
size_t idx) const {
for (int j = bit_width - 4; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
}
}
void emitSelect(std::ostream& s, const std::vector<IData>& indices,
const std::vector<size_t>& idxWidths) const {
for (size_t idx = 0; idx < indices.size(); ++idx) s << "(select ";
const size_t num_indices = idxWidths.size();
size_t wide_size = 0;

for (size_t idx = 0; idx < num_indices; ++idx) s << "(select ";
s << name();
for (size_t idx = 0; idx < indices.size(); ++idx) {
s << " #x";

for (size_t idx = 0; idx < num_indices; ++idx) {
const size_t bit_width = idxWidths[idx];
for (int j = bit_width - 4; j >= 0; j -= 4) {
s << "0123456789abcdef"[(indices[idx] >> j) & 0xf];
s << " #x";

const size_t emit_count = (bit_width > 32) ? (idxWidths[idx] / 32) : 1;

for (size_t i = 0; i < emit_count; ++i) {
emitHexs(s, indices, (bit_width > 32) ? 32 : bit_width, wide_size + i);
}

wide_size += (idxWidths[idx] > 32) ? (idxWidths[idx] / 32) : 1;
s << ")";
}
}
Expand All @@ -129,7 +146,7 @@ class VlRandomArrayVarTemplate final : public VlRandomVar {
const std::string indexed_name = name() + std::to_string(i);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
const std::vector<IData>& indices = it->second->m_indices;
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
Expand Down Expand Up @@ -165,7 +182,7 @@ class VlRandomArrayVarTemplate final : public VlRandomVar {
const std::string indexed_name = name() + std::to_string(j);
const auto it = m_arrVarsRefp->find(indexed_name);
if (it != m_arrVarsRefp->end()) {
const std::vector<size_t>& indices = it->second->m_indices;
const std::vector<IData>& indices = it->second->m_indices;
const std::vector<size_t>& idxWidths = it->second->m_idxWidths;
emitSelect(s, indices, idxWidths);
} else {
Expand All @@ -176,7 +193,6 @@ class VlRandomArrayVarTemplate final : public VlRandomVar {
};
//=============================================================================
// VlRandomizer is the object holding constraints and variable references.

class VlRandomizer final {
// MEMBERS
std::vector<std::string> m_constraints; // Solver-dependent constraints
Expand All @@ -200,45 +216,89 @@ class VlRandomizer final {
bool next(VlRNG& rngr);

template <typename T_Key>
typename std::enable_if<std::is_integral<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
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,
const std::string& base_name, size_t& idx_width) {
integral_index = static_cast<size_t>(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
integral_index.push_back(static_cast<size_t>(key));
indexed_name
= base_name + "[" + std::to_string(integral_index[integral_index.size() - 1]) + "]";
idx_width = sizeof(T_Key) * 8;
}
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,
const std::string& base_name, size_t& idx_width) {
constexpr size_t segment_bits = 32;
constexpr T_Key mask = (static_cast<T_Key>(1) << segment_bits) - 1;
integral_index.push_back(static_cast<size_t>(key >> segment_bits));
integral_index.push_back(static_cast<size_t>(key & mask));

std::ostringstream hex_stream;
hex_stream << std::hex << key;
std::string index_string = hex_stream.str();
index_string.erase(0, index_string.find_first_not_of('0'));
index_string = index_string.empty() ? "0" : index_string;

indexed_name = base_name + "[" + index_string + "]";

idx_width = sizeof(T_Key) * 8;
}
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,
const std::string& base_name, size_t& idx_width) {
std::ostringstream hex_stream;
for (size_t i = key.size(); i > 0; --i) {
const size_t segment_value = key.at(i - 1);
hex_stream << std::hex << segment_value;
integral_index.push_back(segment_value);
}
std::string index_string = hex_stream.str();
index_string.erase(0, index_string.find_first_not_of('0'));
index_string = index_string.empty() ? "0" : index_string;

indexed_name = base_name + "[" + index_string + "]";
idx_width = key.size() * 32;
}
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, size_t& integral_index,
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
integral_index = string_to_integral(key);
indexed_name = base_name + "[" + std::to_string(integral_index) + "]";
idx_width = 64; // 64-bit mask
// Convert the input string to its ASCII hexadecimal representation
std::ostringstream oss;
for (unsigned char c : key) {
oss << std::hex << std::setw(2) << std::setfill('0') << static_cast<int>(c);
}
std::string hex_str = oss.str();
// Ensure the hex string is exactly 128 bits (32 hex characters)
hex_str = hex_str.size() > 32 ? hex_str.substr(0, 32)
: std::string(32 - hex_str.size(), '0') + hex_str;

// Split the hex string into 4 segments (32-bit per segment)
integral_index.clear();
for (size_t i = 0; i < hex_str.size(); i += 8) {
integral_index.push_back(std::stoul(hex_str.substr(i, 8), nullptr, 16));
}

indexed_name = base_name + "["
+ (hex_str.find_first_not_of('0') == std::string::npos
? "0"
: hex_str.substr(hex_str.find_first_not_of('0')))
+ "]";

idx_width = 128;
}
template <typename T_Key>
typename std::enable_if<!std::is_integral<T_Key>::value
&& !std::is_same<T_Key, std::string>::value>::type
process_key(const T_Key& key, std::string& indexed_name, size_t& integral_index,
&& !std::is_same<T_Key, std::string>::value
&& !VlIsVlWide<T_Key>::value>::type
process_key(const T_Key& key, std::string& indexed_name, std::vector<size_t>& integral_index,
const std::string& base_name, size_t& idx_width) {
VL_FATAL_MT(__FILE__, __LINE__, "randomize",
"Unsupported: Only integral and string index of associative array is "
"supported currently.");
}

uint64_t string_to_integral(const std::string& str) {
uint64_t result = 0;
for (char c : str) { result = (result << 8) | static_cast<uint64_t>(c); }

#ifdef VL_DEBUG
if (seen_values.count(result) > 0 && seen_values[result] != str)
VL_WARN_MT(__FILE__, __LINE__, "randomize",
"Conflict detected: Different strings mapped to the same 64-bit index.");
seen_values[result] = str;
#endif

return result;
}

template <typename T>
void write_var(T& var, int width, const char* name, int dimension,
std::uint32_t randmodeIdx = std::numeric_limits<std::uint32_t>::max()) {
Expand Down Expand Up @@ -296,14 +356,14 @@ class VlRandomizer final {

template <typename T>
void record_arr_table(T& var, const std::string name, int dimension,
std::vector<size_t> indices, std::vector<size_t> idxWidths) {
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<size_t> indices, std::vector<size_t> idxWidths) {
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) {
Expand All @@ -316,7 +376,7 @@ class VlRandomizer final {
}
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<size_t> indices, std::vector<size_t> idxWidths) {
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) {
Expand All @@ -330,20 +390,27 @@ class VlRandomizer final {
}
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<size_t> indices, std::vector<size_t> idxWidths) {
std::vector<IData> indices, std::vector<size_t> idxWidths) {
if ((dimension > 0) && (var.size() != 0)) {
for (auto it = var.begin(); it != var.end(); ++it) {
const T_Key& key = it->first;
const T_Value& value = it->second;

std::string indexed_name;
size_t integral_index;
size_t idx_width;
std::vector<size_t> integral_index;
size_t idx_width = 0;

process_key(key, indexed_name, integral_index, name, idx_width);

// Update indices and widths
idxWidths.push_back(idx_width);
indices.push_back(integral_index);
indices.insert(indices.end(), integral_index.begin(), integral_index.end());

record_arr_table(var.at(key), indexed_name, dimension - 1, indices, idxWidths);

// Cleanup indices and widths
idxWidths.pop_back();
indices.pop_back();
indices.resize(indices.size() - integral_index.size());
}
}
}
Expand Down
1 change: 1 addition & 0 deletions include/verilated_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -451,6 +451,7 @@ struct VlWide final {
// METHODS
const EData& at(size_t index) const { return m_storage[index]; }
EData& at(size_t index) { return m_storage[index]; }
size_t size() const { return N_Words; }
WData* data() { return &m_storage[0]; }
const WData* data() const { return &m_storage[0]; }
bool operator<(const VlWide<N_Words>& rhs) const {
Expand Down
18 changes: 10 additions & 8 deletions src/V3Active.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -349,7 +349,7 @@ class ActiveLatchCheckVisitor final : public VNVisitorConst {

class ActiveDlyVisitor final : public VNVisitor {
public:
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL };
enum CheckType : uint8_t { CT_SEQ, CT_COMB, CT_INITIAL, CT_SUSPENDABLE };

private:
// MEMBERS
Expand All @@ -358,7 +358,7 @@ class ActiveDlyVisitor final : public VNVisitor {
// VISITORS
void visit(AstAssignDly* nodep) override {
// Non-blocking assignments are OK in sequential processes
if (m_check == CT_SEQ) return;
if (m_check == CT_SEQ || m_check == CT_SUSPENDABLE) return;

// Issue appropriate warning
if (m_check == CT_INITIAL) {
Expand Down Expand Up @@ -477,21 +477,23 @@ class ActiveVisitor final : public VNVisitor {
AstActive* const wantactivep
= !m_clockedProcess ? m_namer.getSpecialActive<AstSenItem::Combo>(nodep->fileline())
: oldsensesp ? m_namer.getActive(nodep->fileline(), oldsensesp)
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());

// Delete sensitivity list
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);
// Clocked, no sensitivity lists, it's a suspendable, put it in initial
: m_namer.getSpecialActive<AstSenItem::Initial>(nodep->fileline());

// Move node to new active
nodep->unlinkFrBack();
wantactivep->addStmtsp(nodep);

// Warn and convert any delayed assignments
{
ActiveDlyVisitor{nodep, m_clockedProcess ? ActiveDlyVisitor::CT_SEQ
: ActiveDlyVisitor::CT_COMB};
ActiveDlyVisitor{nodep, !m_clockedProcess ? ActiveDlyVisitor::CT_COMB
: oldsensesp ? ActiveDlyVisitor::CT_SEQ
: ActiveDlyVisitor::CT_SUSPENDABLE};
}

// Delete sensitivity list
if (oldsensesp) VL_DO_DANGLING(oldsensesp->deleteTree(), oldsensesp);

// check combinational processes for latches
if (!m_clockedProcess || kwd == VAlwaysKwd::ALWAYS_LATCH) {
const ActiveLatchCheckVisitor latchvisitor{nodep, kwd == VAlwaysKwd::ALWAYS_LATCH};
Expand Down
Loading