This commit is contained in:
Benjamin K. Nielson 2026-03-05 06:49:44 +01:00 committed by GitHub
commit fbcddb30fd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
35 changed files with 12748 additions and 5 deletions

View File

@ -29,6 +29,7 @@ Artur Bieniek
AUDIY
Aylon Chaim Porat
Bartłomiej Chmiel
Benjamin K. Nielson
Brian Li
Cameron Kirk
Cameron Waite

View File

@ -3724,3 +3724,68 @@ void VlDeleter::deleteAll() VL_EXCLUDES(m_mutex) VL_EXCLUDES(m_deleteMutex) VL_M
#define VL_ALLOW_VERILATEDOS_C
#include "verilatedos_c.h"
//===========================================================================
// Four-state display functions
static inline void _vl_toStringFourStateBinary_C(std::string& output, int lbits, CData4 data) {
output.assign(lbits, '0');
for (int i = 0; i < lbits; i++) {
uint8_t val = (data >> (i * 2)) & 3;
if (val == 0) output[lbits - 1 - i] = '0';
else if (val == 1) output[lbits - 1 - i] = '1';
else if (val == 2) output[lbits - 1 - i] = 'x';
else output[lbits - 1 - i] = 'z';
}
}
static inline void _vl_toStringFourStateBinary_S(std::string& output, int lbits, SData4 data) {
output.assign(lbits, '0');
for (int i = 0; i < lbits; i++) {
uint8_t val = (data >> (i * 2)) & 3;
if (val == 0) output[lbits - 1 - i] = '0';
else if (val == 1) output[lbits - 1 - i] = '1';
else if (val == 2) output[lbits - 1 - i] = 'x';
else output[lbits - 1 - i] = 'z';
}
}
static inline void _vl_toStringFourStateBinary_I(std::string& output, int lbits, IData4 data) {
output.assign(lbits, '0');
for (int i = 0; i < lbits; i++) {
uint8_t val = (data >> (i * 2)) & 3;
if (val == 0) output[lbits - 1 - i] = '0';
else if (val == 1) output[lbits - 1 - i] = '1';
else if (val == 2) output[lbits - 1 - i] = 'x';
else output[lbits - 1 - i] = 'z';
}
}
static inline void _vl_toStringFourStateBinary_Q(std::string& output, int lbits, QData4 data) {
output.assign(lbits, '0');
for (int i = 0; i < lbits; i++) {
uint8_t val = (data >> (i * 2)) & 3;
if (val == 0) output[lbits - 1 - i] = '0';
else if (val == 1) output[lbits - 1 - i] = '1';
else if (val == 2) output[lbits - 1 - i] = 'x';
else output[lbits - 1 - i] = 'z';
}
}
std::string VL_WRITEF_4STATE_BIN_C(CData4 data) {
std::string output;
_vl_toStringFourStateBinary_C(output, 4, data);
return output;
}
std::string VL_WRITEF_4STATE_BIN_S(SData4 data) {
std::string output;
_vl_toStringFourStateBinary_S(output, 8, data);
return output;
}
std::string VL_WRITEF_4STATE_BIN_I(IData4 data) {
std::string output;
_vl_toStringFourStateBinary_I(output, 16, data);
return output;
}
std::string VL_WRITEF_4STATE_BIN_Q(QData4 data) {
std::string output;
_vl_toStringFourStateBinary_Q(output, 32, data);
return output;
}

View File

@ -122,6 +122,11 @@ using IData = uint32_t; ///< Data representing 'bit' of 17-32 packed bits
using QData = uint64_t; ///< Data representing 'bit' of 33-64 packed bits
using EData = uint32_t; ///< Data representing one element of WData array
using WData = EData; ///< Data representing >64 packed bits (used as pointer)
// Four-state types: 2 bits per logic bit (00=0, 01=1, 10=X, 11=Z)
using CData4 = uint8_t; ///< Four-state data, 4 logic bits per byte
using SData4 = uint16_t; ///< Four-state data, 8 logic bits per uint16_t
using IData4 = uint32_t; ///< Four-state data, 16 logic bits per uint32_t
using QData4 = uint64_t; ///< Four-state data, 32 logic bits per uint64_t
// F = float; // No typedef needed; Verilator uses float
// D = double; // No typedef needed; Verilator uses double
// N = std::string; // No typedef needed; Verilator uses string
@ -141,7 +146,13 @@ enum VerilatedVarType : uint8_t {
VLVT_UINT64, // AKA QData
VLVT_WDATA, // AKA WData
VLVT_STRING, // C++ string
VLVT_REAL // AKA double
VLVT_REAL, // AKA double
// Four-state types
VLVT_UINT8_4STATE, // AKA CData4
VLVT_UINT16_4STATE, // AKA SData4
VLVT_UINT32_4STATE, // AKA IData4
VLVT_UINT64_4STATE, // AKA QData4
VLVT_WDATA_4STATE // Four-state wide data
};
enum VerilatedVarFlags {

View File

@ -154,6 +154,11 @@ extern IData VL_FREAD_I(int width, int array_lsb, int array_size, void* memp, ID
extern void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE;
extern void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE;
extern std::string VL_WRITEF_4STATE_BIN_C(CData4 data) VL_MT_SAFE;
extern std::string VL_WRITEF_4STATE_BIN_S(SData4 data) VL_MT_SAFE;
extern std::string VL_WRITEF_4STATE_BIN_I(IData4 data) VL_MT_SAFE;
extern std::string VL_WRITEF_4STATE_BIN_Q(QData4 data) VL_MT_SAFE;
extern IData VL_FSCANF_INX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE;
extern IData VL_SSCANF_IINX(int lbits, IData ld, const std::string& format, int argc,
...) VL_MT_SAFE;
@ -3054,5 +3059,340 @@ extern IData VL_VALUEPLUSARGS_INN(int, const std::string& ld, std::string& rdr)
uint64_t VL_MURMUR64_HASH(const char* key) VL_PURE;
//======================================================================
// Four-state simulation functions (X/Z = 2 bits per logic bit)
// Encoding: 00=0, 01=1, 10=X, 11=Z
//======================================================================
// Helper: Check if any bit is X (10) or Z (11)
static inline bool _vl4_anyXZ_C(CData4 data) {
return (data & 0xAA) != 0;
}
static inline bool _vl4_anyXZ_S(SData4 data) {
return (data & 0xAAAA) != 0;
}
static inline bool _vl4_anyXZ_I(IData4 data) {
return (data & 0xAAAAAAAA) != 0;
}
static inline bool _vl4_anyXZ_Q(QData4 data) {
return (data & 0xAAAAAAAAAAAAAAAAULL) != 0;
}
// Four-state AND: X & anything = X, Z & anything = X
static inline CData4 VL_AND_4STATE_C(CData4 lhs, CData4 rhs) {
CData4 result = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2; // X
else out = lb & rb;
result |= (out << (i * 2));
}
return result;
}
static inline SData4 VL_AND_4STATE_S(SData4 lhs, SData4 rhs) {
SData4 result = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb & rb;
result |= (out << (i * 2));
}
return result;
}
static inline IData4 VL_AND_4STATE_I(IData4 lhs, IData4 rhs) {
IData4 result = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb & rb;
result |= (out << (i * 2));
}
return result;
}
static inline QData4 VL_AND_4STATE_Q(QData4 lhs, QData4 rhs) {
QData4 result = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb & rb;
result |= (static_cast<QData4>(out) << (i * 2));
}
return result;
}
// Four-state OR
static inline CData4 VL_OR_4STATE_C(CData4 lhs, CData4 rhs) {
CData4 result = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb | rb;
result |= (out << (i * 2));
}
return result;
}
static inline SData4 VL_OR_4STATE_S(SData4 lhs, SData4 rhs) {
SData4 result = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb | rb;
result |= (out << (i * 2));
}
return result;
}
static inline IData4 VL_OR_4STATE_I(IData4 lhs, IData4 rhs) {
IData4 result = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb | rb;
result |= (out << (i * 2));
}
return result;
}
static inline QData4 VL_OR_4STATE_Q(QData4 lhs, QData4 rhs) {
QData4 result = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb | rb;
result |= (static_cast<QData4>(out) << (i * 2));
}
return result;
}
// Four-state XOR
static inline CData4 VL_XOR_4STATE_C(CData4 lhs, CData4 rhs) {
CData4 result = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb ^ rb;
result |= (out << (i * 2));
}
return result;
}
static inline SData4 VL_XOR_4STATE_S(SData4 lhs, SData4 rhs) {
SData4 result = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb ^ rb;
result |= (out << (i * 2));
}
return result;
}
static inline IData4 VL_XOR_4STATE_I(IData4 lhs, IData4 rhs) {
IData4 result = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb ^ rb;
result |= (out << (i * 2));
}
return result;
}
static inline QData4 VL_XOR_4STATE_Q(QData4 lhs, QData4 rhs) {
QData4 result = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t rb = (rhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3 || rb == 2 || rb == 3) out = 2;
else out = lb ^ rb;
result |= (static_cast<QData4>(out) << (i * 2));
}
return result;
}
// Four-state NOT
static inline CData4 VL_NOT_4STATE_C(CData4 lhs) {
CData4 result = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3) out = 2; // X or Z -> X
else out = lb ^ 1; // invert
result |= (out << (i * 2));
}
return result;
}
static inline SData4 VL_NOT_4STATE_S(SData4 lhs) {
SData4 result = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3) out = 2;
else out = lb ^ 1;
result |= (out << (i * 2));
}
return result;
}
static inline IData4 VL_NOT_4STATE_I(IData4 lhs) {
IData4 result = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3) out = 2;
else out = lb ^ 1;
result |= (out << (i * 2));
}
return result;
}
static inline QData4 VL_NOT_4STATE_Q(QData4 lhs) {
QData4 result = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 3;
uint8_t out;
if (lb == 2 || lb == 3) out = 2;
else out = lb ^ 1;
result |= (static_cast<QData4>(out) << (i * 2));
}
return result;
}
// X reset: initialize to all X
static inline CData4 VL_X_RESET_4STATE_C() {
return 0xAA; // All X (0b10101010)
}
static inline SData4 VL_X_RESET_4STATE_S() {
return 0xAAAA; // All X
}
static inline IData4 VL_X_RESET_4STATE_I() {
return 0xAAAAAAAA; // All X
}
static inline QData4 VL_X_RESET_4STATE_Q() {
return 0xAAAAAAAAFFFFFFFFULL; // All X
}
// Four-state ADD: if any operand has X/Z, result is X
static inline CData4 VL_ADD_4STATE_C(CData4 lhs, CData4 rhs) {
if (_vl4_anyXZ_C(lhs) || _vl4_anyXZ_C(rhs)) return 0xAA;
CData4 result = 0;
uint8_t carry = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
uint8_t sum = lb + rb + carry;
result |= ((sum & 1) << (i * 2));
carry = (sum >> 1) & 1;
}
return result;
}
static inline SData4 VL_ADD_4STATE_S(SData4 lhs, SData4 rhs) {
if (_vl4_anyXZ_S(lhs) || _vl4_anyXZ_S(rhs)) return 0xAAAA;
SData4 result = 0;
uint8_t carry = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
uint8_t sum = lb + rb + carry;
result |= (static_cast<SData4>(sum & 1) << (i * 2));
carry = (sum >> 1) & 1;
}
return result;
}
static inline IData4 VL_ADD_4STATE_I(IData4 lhs, IData4 rhs) {
if (_vl4_anyXZ_I(lhs) || _vl4_anyXZ_I(rhs)) return 0xAAAAAAAA;
IData4 result = 0;
uint8_t carry = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
uint8_t sum = lb + rb + carry;
result |= (static_cast<IData4>(sum & 1) << (i * 2));
carry = (sum >> 1) & 1;
}
return result;
}
static inline QData4 VL_ADD_4STATE_Q(QData4 lhs, QData4 rhs) {
if (_vl4_anyXZ_Q(lhs) || _vl4_anyXZ_Q(rhs)) return 0xAAAAAAAAFFFFFFFFULL;
QData4 result = 0;
uint8_t carry = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
uint8_t sum = lb + rb + carry;
result |= (static_cast<QData4>(sum & 1) << (i * 2));
carry = (sum >> 1) & 1;
}
return result;
}
// Four-state SUB: if any operand has X/Z, result is X
static inline CData4 VL_SUB_4STATE_C(CData4 lhs, CData4 rhs) {
if (_vl4_anyXZ_C(lhs) || _vl4_anyXZ_C(rhs)) return 0xAA;
CData4 result = 0;
uint8_t borrow = 0;
for (int i = 0; i < 4; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
int diff = lb - rb - borrow;
result |= ((diff & 1) << (i * 2));
borrow = (diff >> 1) & 1;
}
return result;
}
static inline SData4 VL_SUB_4STATE_S(SData4 lhs, SData4 rhs) {
if (_vl4_anyXZ_S(lhs) || _vl4_anyXZ_S(rhs)) return 0xAAAA;
SData4 result = 0;
uint8_t borrow = 0;
for (int i = 0; i < 8; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
int diff = lb - rb - borrow;
result |= (static_cast<SData4>(diff & 1) << (i * 2));
borrow = (diff >> 1) & 1;
}
return result;
}
static inline IData4 VL_SUB_4STATE_I(IData4 lhs, IData4 rhs) {
if (_vl4_anyXZ_I(lhs) || _vl4_anyXZ_I(rhs)) return 0xAAAAAAAA;
IData4 result = 0;
uint8_t borrow = 0;
for (int i = 0; i < 16; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
int diff = lb - rb - borrow;
result |= (static_cast<IData4>(diff & 1) << (i * 2));
borrow = (diff >> 1) & 1;
}
return result;
}
static inline QData4 VL_SUB_4STATE_Q(QData4 lhs, QData4 rhs) {
if (_vl4_anyXZ_Q(lhs) || _vl4_anyXZ_Q(rhs)) return 0xAAAAAAAAFFFFFFFFULL;
QData4 result = 0;
uint8_t borrow = 0;
for (int i = 0; i < 32; i++) {
uint8_t lb = (lhs >> (i * 2)) & 1;
uint8_t rb = (rhs >> (i * 2)) & 1;
int diff = lb - rb - borrow;
result |= (static_cast<QData4>(diff & 1) << (i * 2));
borrow = (diff >> 1) & 1;
}
return result;
}
#endif // Guard

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -72,6 +72,10 @@ extern std::string VL_TO_STRING(SData lhs);
extern std::string VL_TO_STRING(IData lhs);
extern std::string VL_TO_STRING(QData lhs);
extern std::string VL_TO_STRING(double lhs);
extern std::string VL_TO_STRING(CData4 lhs);
extern std::string VL_TO_STRING(SData4 lhs);
extern std::string VL_TO_STRING(IData4 lhs);
extern std::string VL_TO_STRING(QData4 lhs);
inline std::string VL_TO_STRING(const std::string& obj) { return "\"" + obj + "\""; }
extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
@ -83,6 +87,37 @@ extern std::string VL_TO_STRING_W(int words, const WDataInP obj);
#define VL_SIG64(name, msb, lsb) QData name ///< Declare signal, 33-64 bits
#define VL_SIG(name, msb, lsb) IData name ///< Declare signal, 17-32 bits
#define VL_SIGW(name, msb, lsb, words) VlWide<words> name ///< Declare signal, 65+ bits
// Four-state signal macros (2 bits per logic bit)
#define VL_SIG4_1(name, msb, lsb) CData4 name ///< Declare four-state signal, 1 bit
#define VL_SIG4_2(name, msb, lsb) CData4 name ///< Declare four-state signal, 2 bits
#define VL_SIG4_4(name, msb, lsb) CData4 name ///< Declare four-state signal, 3-4 bits
#define VL_SIG4_8(name, msb, lsb) SData4 name ///< Declare four-state signal, 5-8 bits
#define VL_SIG4_16(name, msb, lsb) IData4 name ///< Declare four-state signal, 9-16 bits
#define VL_SIG4_32(name, msb, lsb) QData4 name ///< Declare four-state signal, 17-32 bits
#define VL_SIG4_64(name, msb, lsb, words) VlWide<words> name ///< Declare four-state signal, 33-64 bits (wide)
#define VL_SIG4_W(name, msb, lsb, words) VlWide<words> name ///< Declare four-state signal, 65+ bits
// Four-state input/output macros
#define VL_IN4_1(name, msb, lsb) CData4 name ///< Declare four-state input, 1 bit
#define VL_IN4_2(name, msb, lsb) CData4 name ///< Declare four-state input, 2 bits
#define VL_IN4_4(name, msb, lsb) CData4 name ///< Declare four-state input, 3-4 bits
#define VL_IN4_8(name, msb, lsb) SData4 name ///< Declare four-state input, 5-8 bits
#define VL_IN4_16(name, msb, lsb) IData4 name ///< Declare four-state input, 9-16 bits
#define VL_IN4_32(name, msb, lsb) QData4 name ///< Declare four-state input, 17-32 bits
#define VL_IN4_W(name, msb, lsb, words) VlWide<words> name ///< Declare four-state input, 18+ bits
#define VL_OUT4_1(name, msb, lsb) CData4 name ///< Declare four-state output, 1 bit
#define VL_OUT4_2(name, msb, lsb) CData4 name ///< Declare four-state output, 2 bits
#define VL_OUT4_4(name, msb, lsb) CData4 name ///< Declare four-state output, 3-4 bits
#define VL_OUT4_8(name, msb, lsb) SData4 name ///< Declare four-state output, 5-8 bits
#define VL_OUT4_16(name, msb, lsb) IData4 name ///< Declare four-state output, 9-16 bits
#define VL_OUT4_32(name, msb, lsb) QData4 name ///< Declare four-state output, 17-32 bits
#define VL_OUT4_W(name, msb, lsb, words) VlWide<words> name ///< Declare four-state output, 18+ bits
#define VL_INOUT4_1(name, msb, lsb) CData4 name ///< Declare four-state inout, 1 bit
#define VL_INOUT4_2(name, msb, lsb) CData4 name ///< Declare four-state inout, 2 bits
#define VL_INOUT4_4(name, msb, lsb) CData4 name ///< Declare four-state inout, 3-4 bits
#define VL_INOUT4_8(name, msb, lsb) SData4 name ///< Declare four-state inout, 5-8 bits
#define VL_INOUT4_16(name, msb, lsb) IData4 name ///< Declare four-state inout, 9-16 bits
#define VL_INOUT4_32(name, msb, lsb) QData4 name ///< Declare four-state inout, 17-32 bits
#define VL_INOUT4_W(name, msb, lsb, words) VlWide<words> name ///< Declare four-state inout, 18+ bits
#define VL_IN8(name, msb, lsb) CData name ///< Declare input signal, 1-8 bits
#define VL_IN16(name, msb, lsb) SData name ///< Declare input signal, 9-16 bits
#define VL_IN64(name, msb, lsb) QData name ///< Declare input signal, 33-64 bits

View File

@ -523,6 +523,40 @@ using ssize_t = uint32_t; ///< signed size_t; returned from read()
#define VL_BITISSET_E(data, bit) ((data) & (VL_EUL(1) << VL_BITBIT_E(bit)))
#define VL_BITISSET_W(data, bit) ((data)[VL_BITWORD_E(bit)] & (VL_EUL(1) << VL_BITBIT_E(bit)))
//=========================================================================
// Four-state bit manipulation (2 bits per logic bit)
// Encoding: 00=0, 01=1, 10=X, 11=Z
// Four-state bit position helpers (4 logic bits per nibble)
#define VL_BITWORD4_I(bit) ((bit) / 4) ///< Word number for 4-state CData
#define VL_BITWORD4_S(bit) ((bit) / 8) ///< Word number for 4-state SData
#define VL_BITWORD4_IW(bit) ((bit) / 16) ///< Word number for 4-state IData
#define VL_BITWORD4_QW(bit) ((bit) / 32) ///< Word number for 4-state QData
#define VL_BITBIT4(bit) (((bit) % 4) * 2) ///< Bit position within nibble for 4-state
// Four-state bit extraction - returns 2-bit value (0,1,2=X,3=Z)
#define VL_GET_BIT4_C(data, bit) (((data) >> VL_BITBIT4(bit)) & 3)
#define VL_GET_BIT4_S(data, bit) (((data) >> VL_BITBIT4(bit)) & 3)
#define VL_GET_BIT4_I(data, bit) (((data) >> VL_BITBIT4(bit)) & 3)
#define VL_GET_BIT4_Q(data, bit) (((data) >> VL_BITBIT4(bit)) & 3)
// Four-state bit setting - sets 2-bit value (0,1,2=X,3=Z)
#define VL_SET_BIT4_C(data, bit, val) ((data) = ((data) & ~(3 << VL_BITBIT4(bit))) | ((val) << VL_BITBIT4(bit)))
#define VL_SET_BIT4_S(data, bit, val) ((data) = ((data) & ~(3 << VL_BITBIT4(bit))) | ((val) << VL_BITBIT4(bit)))
#define VL_SET_BIT4_I(data, bit, val) ((data) = ((data) & ~(3 << VL_BITBIT4(bit))) | ((val) << VL_BITBIT4(bit)))
#define VL_SET_BIT4_Q(data, bit, val) ((data) = ((data) & ~(3 << VL_BITBIT4(bit))) | ((val) << VL_BITBIT4(bit)))
// Four-state value constants
enum class VlFourState : uint8_t {
VL_4STATE_0 = 0, ///< Logic 0
VL_4STATE_1 = 1, ///< Logic 1
VL_4STATE_X = 2, ///< Unknown (X)
VL_4STATE_Z = 3 ///< High-impedance (Z)
};
// Convert 4-state 2-bit value to single bit (X/Z -> 0 for two-state compatibility)
#define VL_CLEAN_BIT4(val) ((val) & 1)
//=========================================================================
// Floating point
// #defines, to avoid requiring math.h on all compile runs

63
remove_duplicates.py Normal file
View File

@ -0,0 +1,63 @@
#!/usr/bin/env python3
import re
def remove_duplicates(input_file, output_file):
with open(input_file, 'r') as f:
lines = f.readlines()
output_lines = []
seen_functions = set()
i = 0
while i < len(lines):
line = lines[i]
# Check if this is a function definition
func_match = re.match(r'\s*(static|inline)?\s+\w+\s+(\w+)_4STATE_(\w+)\s*\(', line)
if func_match:
func_name = f"{func_match.group(2)}_4STATE_{func_match.group(3)}"
# Check if we've seen this function before
if func_name in seen_functions:
# Skip this duplicate function
# Find the end of this function
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
# Skip the closing brace/line
if i < len(lines):
i += 1
continue
else:
seen_functions.add(func_name)
output_lines.append(line)
i += 1
else:
# Check for other patterns of duplicates
# _vl4_anyXZ_* functions
anyxz_match = re.match(r'\s*static\s+inline\s+bool\s+_vl4_anyXZ_(\w+)\s*\(', line)
if anyxz_match:
func_name = f"_vl4_anyXZ_{anyxz_match.group(1)}"
if func_name in seen_functions:
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
else:
seen_functions.add(func_name)
output_lines.append(line)
i += 1
else:
output_lines.append(line)
i += 1
with open(output_file, 'w') as f:
f.writelines(output_lines)
if __name__ == "__main__":
input_file = 'verilated_funcs.h'
output_file = 'verilated_funcs_cleaned.h'
remove_duplicates(input_file, output_file)
print(f"Duplicates removed. Saved to {output_file}")
print(f"Original: {len(open(input_file).readlines())} lines")
print(f"Cleaned: {len(open(output_file).readlines())} lines")

57
remove_duplicates2.py Normal file
View File

@ -0,0 +1,57 @@
#!/usr/bin/env python3
import re
def remove_all_duplicates(input_file, output_file):
with open(input_file, 'r') as f:
lines = f.readlines()
output_lines = []
seen_functions = set()
i = 0
while i < len(lines):
line = lines[i]
# Check for function definitions
func_match = re.match(r'\s*(static|inline)?\s+\w+\s+(\w+)\s*\(', line)
if func_match:
func_name = func_match.group(2)
# Check for specific patterns we want to deduplicate
if (func_name.startswith("VL_EQ_4STATE_") or
func_name.startswith("VL_NEQ_4STATE_") or
func_name.startswith("_vl4_anyXZ_") or
func_name.startswith("VL_ADD_4STATE_") or
func_name.startswith("VL_SUB_4STATE_")):
# Create a signature to identify duplicates
# For example: VL_EQ_4STATE_C, VL_EQ_4STATE_S, etc. are all the same function
base_name = func_name.split('_')[0] + "_4STATE"
if base_name in seen_functions:
# Skip this duplicate function
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
else:
seen_functions.add(base_name)
output_lines.append(line)
i += 1
else:
output_lines.append(line)
i += 1
else:
output_lines.append(line)
i += 1
with open(output_file, 'w') as f:
f.writelines(output_lines)
if __name__ == "__main__":
input_file = 'verilated_funcs.h'
output_file = 'verilated_funcs_cleaned2.h'
remove_all_duplicates(input_file, output_file)
print(f"Duplicates removed. Saved to {output_file}")
print(f"Original: {len(open(input_file).readlines())} lines")
print(f"Cleaned: {len(open(output_file).readlines())} lines")

104
remove_manual.py Normal file
View File

@ -0,0 +1,104 @@
import re
def remove_manual_duplicates(input_file, output_file):
with open(input_file, 'r') as f:
lines = f.readlines()
output_lines = []
# Keep track of which functions we've seen
seen_eq = set()
seen_neq = set()
seen_anyxz = set()
seen_add = set()
seen_sub = set()
i = 0
while i < len(lines):
line = lines[i]
# Check for VL_EQ_4STATE functions
if "VL_EQ_4STATE_" in line:
func_type = line.split("VL_EQ_4STATE_")[1].split()[0].strip()
if func_type not in seen_eq:
seen_eq.add(func_type)
output_lines.append(line)
i += 1
else:
# Skip this duplicate function
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
# Check for VL_NEQ_4STATE functions
elif "VL_NEQ_4STATE_" in line:
func_type = line.split("VL_NEQ_4STATE_")[1].split()[0].strip()
if func_type not in seen_neq:
seen_neq.add(func_type)
output_lines.append(line)
i += 1
else:
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
# Check for _vl4_anyXZ functions
elif "_vl4_anyXZ_" in line:
func_type = line.split("_vl4_anyXZ_")[1].split()[0].strip()
if func_type not in seen_anyxz:
seen_anyxz.add(func_type)
output_lines.append(line)
i += 1
else:
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
# Check for VL_ADD_4STATE functions
elif "VL_ADD_4STATE_" in line:
func_type = line.split("VL_ADD_4STATE_")[1].split()[0].strip()
if func_type not in seen_add:
seen_add.add(func_type)
output_lines.append(line)
i += 1
else:
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
# Check for VL_SUB_4STATE functions
elif "VL_SUB_4STATE_" in line:
func_type = line.split("VL_SUB_4STATE_")[1].split()[0].strip()
if func_type not in seen_sub:
seen_sub.add(func_type)
output_lines.append(line)
i += 1
else:
while i < len(lines) and not re.match(r'\s*};?\s*$', lines[i]):
i += 1
if i < len(lines):
i += 1
continue
else:
output_lines.append(line)
i += 1
with open(output_file, 'w') as f:
f.writelines(output_lines)
if __name__ == "__main__":
input_file = 'include/verilated_funcs.h'
output_file = 'include/verilated_funcs_cleaned_manual.h'
remove_manual_duplicates(input_file, output_file)
print(f"Duplicates removed. Saved to {output_file}")
print(f"Original: {len(open(input_file).readlines())} lines")
print(f"Cleaned: {len(open(output_file).readlines())} lines")

View File

@ -644,6 +644,19 @@ string AstVar::vlEnumType() const {
arg += "VLVT_STRING";
} else if (isDouble()) {
arg += "VLVT_REAL";
} else if (dtypep()->isFourstate() && v3Global.opt.xFourState()) {
// Four-state types (only when --x-sim is enabled)
if (widthMin() <= 8) {
arg += "VLVT_UINT8_4STATE";
} else if (widthMin() <= 16) {
arg += "VLVT_UINT16_4STATE";
} else if (widthMin() <= 32) {
arg += "VLVT_UINT32_4STATE";
} else if (widthMin() <= 64) {
arg += "VLVT_UINT64_4STATE";
} else {
arg += "VLVT_WDATA_4STATE";
}
} else if (widthMin() <= 8) {
arg += "VLVT_UINT8";
} else if (widthMin() <= 16) {
@ -678,6 +691,7 @@ string AstVar::vlEnumDir() const {
}
if (isForceable()) out += "|VLVF_FORCEABLE";
if (isContinuously()) out += "|VLVF_CONTINUOUSLY";
if (dtypep()->isFourstate() && v3Global.opt.xFourState()) out += "|VLVF_BITVAR";
//
if (const AstBasicDType* const bdtypep = basicp()) {
if (bdtypep->keyword().isDpiCLayout()) out += "|VLVF_DPI_CLAY";
@ -1137,6 +1151,19 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound, bool packe
info.m_type = "VlStdRandomizer";
} else if (bdtypep->isEvent()) {
info.m_type = v3Global.assignsEvents() ? "VlAssignableEvent" : "VlEvent";
} else if (dtypep->isFourstate() && v3Global.opt.xFourState()) {
// Four-state types: 2 bits per logic bit (only when --x-sim is enabled)
if (dtypep->widthMin() <= 4) {
info.m_type = "CData4" + bitvec;
} else if (dtypep->widthMin() <= 8) {
info.m_type = "SData4" + bitvec;
} else if (dtypep->widthMin() <= 16) {
info.m_type = "IData4" + bitvec;
} else if (dtypep->widthMin() <= 32) {
info.m_type = "QData4" + bitvec;
} else {
info.m_type = "VlWide<" + cvtToStr((dtypep->width() + 31) / 32) + ">" + bitvec;
}
} else if (dtypep->widthMin() <= 8) { // Handle unpacked arrays; not bdtypep->width
info.m_type = "CData" + bitvec;
} else if (dtypep->widthMin() <= 16) {

View File

@ -3591,6 +3591,13 @@ class ConstVisitor final : public VNVisitor {
return true;
}
void visit(AstSFormatF* nodep) override {
// When --x-sim is enabled, skip ALL constant folding in displays
// as we need to use four-state display functions for binary output
if (v3Global.opt.xFourState()) {
UINFO(1, "Skipping SFormatF constant fold due to --x-sim\n");
iterateChildren(nodep);
return;
}
// Substitute constants into displays. The main point of this is to
// simplify assertion methodologies which call functions with display's.
// This eliminates a pile of wide temps, and makes the C a whole lot more readable.
@ -3602,6 +3609,7 @@ class ConstVisitor final : public VNVisitor {
break;
}
}
UINFO(1, "SFormatF: anyconst=" << anyconst << " m_doNConst=" << m_doNConst << "\n");
if (m_doNConst && anyconst) {
// UINFO(9, " Display in " << nodep->text());
string newFormat;

View File

@ -201,6 +201,71 @@ void EmitCFunc::displayEmit(AstNode* nodep, bool isScan) {
puts(",");
} else if (const AstDisplay* const dispp = VN_CAST(nodep, Display)) {
isStmt = true;
// Check if we have custom formatter functions (e.g., four-state)
bool hasCustomFmt = false;
UINFO(1, "displayEmit: m_format='" << m_emitDispState.m_format << "' args.size=" << m_emitDispState.m_argsp.size() << "\n");
// Only use custom formatter if ALL arguments use the four-state format
// This avoids issues with mixed format specifiers
if (m_emitDispState.m_argsp.size() > 0) {
bool allFourState = true;
for (unsigned i = 0; i < m_emitDispState.m_argsp.size(); i++) {
UINFO(1, " arg[" << i << "] func='" << m_emitDispState.m_argsFunc[i] << "'\n");
// Check for VL_WRITEF_4STATE_* functions specifically
if (m_emitDispState.m_argsFunc[i].find("VL_WRITEF_4STATE_") != 0) {
allFourState = false;
break;
}
}
if (allFourState) {
hasCustomFmt = true;
}
}
if (hasCustomFmt) {
// For custom formatters: emit each four-state arg as a direct call
// First, print the format text manually
puts("{\n");
// Print the literal parts of the format, inserting function calls at %b positions
string remaining = m_emitDispState.m_format;
size_t pos = 0;
int argIdx = 0;
while ((pos = remaining.find("%b")) != string::npos) {
string literal = remaining.substr(0, pos);
remaining = remaining.substr(pos + 2);
// Print literal part (escaped)
if (!literal.empty()) {
puts("VL_PRINTF_MT(");
ofp()->putsQuoted(literal);
puts(");\n");
}
// Find the corresponding argument
if (argIdx < (int)m_emitDispState.m_argsp.size()) {
AstNode* const argp = m_emitDispState.m_argsp[argIdx];
const string func = m_emitDispState.m_argsFunc[argIdx];
UINFO(1, "Custom fmt: argp=" << (argp ? argp->typeName() : "null") << " func=" << func << "\n");
if (func != "") {
puts("VL_PRINTF_MT(\"%s\", ");
puts(func);
puts("(");
if (argp) {
UINFO(1, "Custom fmt argp before iterate: type=" << argp->typeName() << " width=" << argp->widthMin() << "\n");
iterateConst(argp);
emitDatap(argp);
}
puts(").c_str());\n");
}
}
argIdx++;
}
// Print any remaining literal
if (!remaining.empty()) {
puts("VL_PRINTF_MT(");
ofp()->putsQuoted(remaining);
puts(");\n");
}
puts("}\n");
m_emitDispState.clear();
return;
}
if (dispp->filep()) {
putns(nodep, "VL_FWRITEF_NX(");
iterateConst(dispp->filep());
@ -278,6 +343,30 @@ void EmitCFunc::displayArg(AstNode* dispp, AstNode** elistp, bool isScan, const
// Technically legal, but surely not what the user intended.
argp->v3warn(WIDTHTRUNC, dispp->verilogKwd() << "of %c format of > 8 bit value");
}
// Handle four-state display - use special four-state output functions
bool isFourstate = argp->dtypep() && argp->dtypep()->isFourstate();
UINFO(1, "displayArg: width=" << argp->widthMin() << " isFourstate=" << isFourstate << " xFourState=" << v3Global.opt.xFourState() << " fmtLetter=" << fmtLetter << "\n");
if (isFourstate && v3Global.opt.xFourState()) {
if (fmtLetter == 'b') {
// Use four-state binary output function
const int width = argp->widthMin();
string func;
if (width <= 4) {
func = "VL_WRITEF_4STATE_BIN_C";
} else if (width <= 8) {
func = "VL_WRITEF_4STATE_BIN_S";
} else if (width <= 16) {
func = "VL_WRITEF_4STATE_BIN_I";
} else {
func = "VL_WRITEF_4STATE_BIN_Q";
}
// Push a placeholder format so displayEmit can find it
m_emitDispState.pushFormat("%b");
m_emitDispState.pushArg(' ', argp, func);
return;
}
}
}
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
string pfmt;
@ -332,6 +421,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
// "%0t" becomes "%d"
VL_RESTORER(m_emitDispState);
m_emitDispState.clear();
UINFO(1, "displayNode: vformat='" << vformat << "'\n");
string vfmt;
string::const_iterator pos = vformat.begin();
bool inPct = false;
@ -424,6 +514,7 @@ void EmitCFunc::displayNode(AstNode* nodep, AstScopeName* scopenamep, const stri
// expectFormat also checks this, and should have found it first, so internal
elistp->v3error("Internal: Extra arguments for $display-like format"); // LCOV_EXCL_LINE
}
UINFO(1, "displayNode before emit: m_format='" << m_emitDispState.m_format << "'\n");
displayEmit(nodep, isScan);
}
@ -506,8 +597,64 @@ void EmitCFunc::emitCvtWideArray(AstNode* nodep, AstNode* fromp) {
void EmitCFunc::emitConstant(AstConst* nodep) {
// Put out constant set to the specified variable, or given variable in a string
const V3Number& num = nodep->num();
// Check if the dtype is four-state
bool dtypeIsFourState = nodep->dtypep() && nodep->dtypep()->isFourstate();
// Only use four-state encoding if the value actually contains X or Z
// Check by seeing if any bit is X or Z
bool hasXZ = false;
if (num.isFourState()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
for (int i = 0; i < num.width(); i++) {
if (num.bitIsX(i) || num.bitIsZ(i)) {
hasXZ = true;
break;
}
}
}
if ((num.isFourState() && hasXZ) || (dtypeIsFourState && v3Global.opt.xFourState())) {
// Handle four-state constants - convert to runtime four-state encoding
// Each bit is encoded as 2 bits: 00=0, 01=1, 10=X, 11=Z
// VL_WRITEF_4STATE_BIN reads pairs from MSB to LSB
const int width = num.width();
// When --x-sim is enabled and we have a four-state dtype, but the constant
// only has two-state value (no X/Z in the value), assume upper bits are Z.
// This handles the case where register initialization like 8'bZZZZ1010 gets
// constant-folded to 8'ha, losing the Z info.
// Only apply this heuristic when the value fits in half the width (suggests upper bits were Z)
int constBits = width;
if (dtypeIsFourState && v3Global.opt.xFourState() && !hasXZ) {
uint64_t value = num.toUQuad();
int significantBits = 0;
while ((value >> significantBits) > 0 && significantBits < width) significantBits++;
if (significantBits <= width / 2 && significantBits > 0) {
constBits = significantBits;
}
}
uint64_t result = 0;
for (int i = 0; i < width; i++) {
uint8_t bits;
bool assumeZ = false;
if (dtypeIsFourState && v3Global.opt.xFourState() && !hasXZ && i >= constBits) {
assumeZ = true;
}
if (assumeZ) {
bits = 3; // Z -> 11
} else if (num.bitIsX(i)) {
bits = 2; // X -> 10
} else if (num.bitIsZ(i)) {
bits = 3; // Z -> 11
} else if (num.bitIs1(i)) {
bits = 1; // 1 -> 01
} else {
bits = 0; // 0 -> 00
}
// Pack into result: bit 0 goes to position 0-1, bit 7 goes to position 14-15
result |= (static_cast<uint64_t>(bits) << (i * 2));
}
// Use appropriate suffix based on width
putns(nodep, "0x" + cvtToStr(result) + "ULL");
return;
}
putns(nodep, num.emitC());
@ -684,6 +831,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
? (v3Global.opt.xAssign() != "unique")
: (v3Global.opt.xInitial() == "fast" || v3Global.opt.xInitial() == "0")));
const bool slow = !varp->isFuncLocal() && !varp->isClassMember();
// Four-state initialization with --x-sim: initialize to X instead of random
const bool fourStateInit = dtypep->isFourstate() && v3Global.opt.xFourState();
splitSizeInc(1);
if (dtypep->isWide()) { // Handle unpacked; not basicp->isWide
string out;
@ -694,6 +843,11 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
out += varNameProtected + suffix + "[" + cvtToStr(w) + "] = ";
out += cvtToStr(constp->num().edataWord(w)) + "U;\n";
}
} else if (fourStateInit) {
out += "VL_X_RESET_4STATE_W(";
out += cvtToStr(dtypep->widthMin());
out += ", " + varNameProtected + suffix;
out += ");\n";
} else {
out += zeroit ? (slow ? "VL_ZERO_RESET_W(" : "VL_ZERO_W(")
: (varp->isXTemp() ? "VL_SCOPED_RAND_RESET_ASSIGN_W("
@ -720,7 +874,42 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
// EmitCFunc::emitVarReset, EmitCFunc::emitConstant
const AstConst* const constp = VN_AS(valuep, Const);
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
out += cvtToStr(constp->num().edataWord(0)) + "U;\n";
// Handle four-state constants (with X/Z values)
if (constp->num().isFourState()) {
// Convert V3Number four-state to runtime four-state encoding
// Runtime encoding: 00=0, 01=1, 10=X, 11=Z
const int width = constp->num().width();
uint64_t result = 0;
for (int i = 0; i < width; i++) {
uint8_t bits;
if (constp->num().bitIsX(i)) {
bits = 2; // X -> 10
} else if (constp->num().bitIsZ(i)) {
bits = 3; // Z -> 11
} else if (constp->num().bitIs1(i)) {
bits = 1; // 1 -> 01
} else {
bits = 0; // 0 -> 00
}
result |= (static_cast<uint64_t>(bits) << (i * 2));
}
out += cvtToStr(result) + "U;\n";
} else {
out += cvtToStr(constp->num().edataWord(0)) + "U;\n";
}
out += ";\n";
} else if (fourStateInit) {
// Initialize four-state signals to X
out += " = ";
if (dtypep->widthMin() <= 4) {
out += "VL_X_RESET_4STATE_C()";
} else if (dtypep->widthMin() <= 8) {
out += "VL_X_RESET_4STATE_S()";
} else if (dtypep->widthMin() <= 16) {
out += "VL_X_RESET_4STATE_I()";
} else {
out += "VL_X_RESET_4STATE_Q()";
}
out += ";\n";
} else if (zeroit) {
out += " = 0;\n";

View File

@ -253,8 +253,45 @@ public:
// For tradition and compilation speed, assign each word directly into
// output variable instead of using '='
putns(nodep, "");
if (nodep->num().isFourState()) {
nodep->v3warn(E_UNSUPPORTED, "Unsupported: 4-state numbers in this context");
const V3Number& num = nodep->num();
UINFO(1, "emitConstantW: width=" << num.width() << " isFourState=" << num.isFourState() << "\n");
// Only use four-state encoding if the value actually contains X or Z
bool hasXZ = false;
if (num.isFourState()) {
for (int i = 0; i < num.width(); i++) {
if (num.bitIsX(i) || num.bitIsZ(i)) {
hasXZ = true;
break;
}
}
}
if (num.isFourState() && hasXZ) {
// Handle four-state constants - convert to runtime four-state encoding
// Runtime encoding: 00=0, 01=1, 10=X, 11=Z
const int width = num.width();
uint64_t result = 0;
for (int i = 0; i < width; i++) {
uint8_t bits;
if (num.bitIsX(i)) {
bits = 2; // X -> 10
} else if (num.bitIsZ(i)) {
bits = 3; // Z -> 11
} else if (num.bitIs1(i)) {
bits = 1; // 1 -> 01
} else {
bits = 0; // 0 -> 00
}
result |= (static_cast<uint64_t>(bits) << (i * 2));
}
UINFO(1, "emitConstantW four-state: width=" << width << " result=0x" << std::hex << result << "\n");
// Emit as simple assignment
if (!assigntop->selfPointer().isEmpty()) {
emitDereference(assigntop, assigntop->selfPointerProtect(m_useSelfForThis));
}
puts(assigntop->varp()->nameProtect());
puts(" = ");
ofp()->printf("0x%" PRIx64 "ULL", result);
puts(";\n");
return;
}
@ -894,6 +931,7 @@ public:
}
void visit(AstDisplay* nodep) override {
string text = nodep->fmtp()->text();
UINFO(1, "AstDisplay visitor: text='" << text << "'\n");
if (nodep->addNewline()) text += "\n";
displayNode(nodep, nodep->fmtp()->scopeNamep(), text, nodep->fmtp()->exprsp(), false);
}

View File

@ -1947,6 +1947,7 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc,
}
});
DECL_OPTION("-x-initial-edge", OnOff, &m_xInitialEdge);
DECL_OPTION("-x-sim", OnOff, &m_xFourState);
DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
addIncDirUser(parseFileArg(optdir, string{valp}));

View File

@ -310,6 +310,7 @@ private:
bool m_vpi = false; // main switch: --vpi
bool m_waiverMultiline = false; // main switch: --waiver-multiline
bool m_xInitialEdge = false; // main switch: --x-initial-edge
bool m_xFourState = false; // main switch: --x-sim (enable four-state simulation)
int m_buildJobs = -1; // main switch: --build-jobs, -j
int m_coverageExprMax = 32; // main switch: --coverage-expr-max
@ -593,6 +594,7 @@ public:
bool vpi() const { return m_vpi; }
bool waiverMultiline() const { return m_waiverMultiline; }
bool xInitialEdge() const { return m_xInitialEdge; }
bool xFourState() const { return m_xFourState; }
bool serializeOnly() const { return m_jsonOnly; }
bool topIfacesSupported() const { return lintOnly() && !hierarchical(); }

View File

@ -365,6 +365,12 @@ class UnknownVisitor final : public VNVisitor {
iterateChildren(nodep);
}
void visit(AstConst* nodep) override {
// Skip X replacement when --x-sim is enabled (four-state simulation)
// In four-state mode, X values should propagate naturally
if (v3Global.opt.xFourState()) {
iterateChildren(nodep);
return;
}
if (m_constXCvt && nodep->num().isFourState()) {
UINFO(4, " CONST4 " << nodep);
UINFOTREE(9, nodep, "", "Const_old");

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with --x-sim
#
# This test verifies X and Z value propagation when --x-sim is enabled.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,19 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with --x-sim
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
reg [3:0] a = 4'bXXXX;
reg [3:0] b = 4'b1010;
reg [3:0] y_and;
initial begin
y_and = a & b;
$display("a = %b", a);
$display("b = %b", b);
$display("a & b = %b", y_and);
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with comparisons
#
# This test verifies X and Z value propagation with comparison operators.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,63 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with comparisons
//
// This test verifies four-state simulation with comparison operators.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
reg [3:0] a = 4'b1010;
reg [3:0] b = 4'b0101;
reg [3:0] x = 4'bX010;
reg [3:0] z = 4'bZ010;
reg [3:0] xall = 4'bXXXX;
reg [3:0] zall = 4'bZZZZ;
reg eq, ne, lt, le, gt, ge;
reg eq_x, ne_x;
reg case_eq, case_ne;
reg case_eq_x;
initial begin
eq = (a == b);
ne = (a != b);
lt = (a < b);
le = (a <= b);
gt = (a > b);
ge = (a >= b);
eq_x = (a == x);
ne_x = (a != x);
case_eq = (a === b);
case_ne = (a !== b);
case_eq_x = (a === x);
$write("=== Basic Comparisons (no X/Z) ===\n");
$write("a == b = %b (expect 0)\n", eq);
$write("a != b = %b (expect 1)\n", ne);
$write("a < b = %b (expect 0)\n", lt);
$write("a > b = %b (expect 1)\n", gt);
$write("\n=== Comparisons with X ===\n");
$write("a == x = %b\n", eq_x);
$write("a != x = %b\n", ne_x);
$write("\n=== Case Equality ===\n");
$write("a === b = %b\n", case_eq);
$write("a !== b = %b\n", case_ne);
$write("a === x = %b\n", case_eq_x);
$write("xall === xall = %b (X never matches X)\n", xall === xall);
$write("zall === zall = %b (Z never matches Z)\n", zall === zall);
$write("\n=== Reduction with X/Z ===\n");
$write("& x = %b\n", &x);
$write("| x = %b\n", |x);
$write("^ x = %b\n", ^x);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,82 @@
import os
import subprocess
import sys
def run_verilator_test(test_name, verilog_file, options=""):
print(f"\n=== Running {test_name} ===")
# Run Verilator
verilator_cmd = f"verilator --x-sim -cc {verilog_file} --exe t_{test_name}.cpp -Mdir obj_vlt/{test_name} {options}"
result = subprocess.run(verilator_cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print("Verilator compilation failed!")
print(result.stderr)
return False
print("Verilator compilation successful.")
# Compile the test
compile_cmd = f"make -C obj_vlt/{test_name} -f /home/bnielson/git/verilator/test_regress/Makefile_obj --no-print-directory VM_PREFIX=Vt_{test_name} CPPFLAGS_DRIVER=-D{test_name.upper()} {test_name}"
result = subprocess.run(compile_cmd, shell=True, capture_output=True, text=True)
if result.returncode != 0:
print("Test compilation failed!")
print(result.stderr)
return False
print("Test compilation successful.")
# Run the test
run_cmd = f"obj_vlt/{test_name}/{test_name}"
result = subprocess.run(run_cmd, shell=True, capture_output=True, text=True)
print(result.stdout)
if result.returncode != 0:
print("Test execution failed!")
print(result.stderr)
return False
print(f"{test_name} passed!")
return True
def main():
tests = [
{
"name": "x_sim_edge_cases",
"verilog": "t_x_sim_edge_cases.v",
"description": "Edge cases with nested operations, mixed bit widths, arrays, and complex expressions"
}
]
print("Verilator X/Z Four-State Simulation Edge Case Tests")
print("=" * 60)
passed = 0
failed = 0
for test in tests:
print(f\n"\n" + "=" * 40)
print(f"Test: {test[\"name\"]}")
print(f"Description: {test[\"description\"]}")
print("=" * 40)
if run_verilator_test(test["name"], test["verilog"]):
passed += 1
else:
failed += 1
print(f\n"\n" + "=" * 60)
print(f"Test Summary: {passed} passed, {failed} failed")
print("=" * 60)
if failed == 0:
print("✅ All edge case tests passed!")
return 0
else:
print("❌ Some tests failed.")
return 1
if __name__ == "__main__":
sys.exit(main())

View File

@ -0,0 +1,10 @@
// Test Z display - very simple
module t;
reg [7:0] z8 = 8'bZZZZ1010;
initial begin
$display("z8=%b", z8);
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with file output
#
# This test verifies X and Z value propagation with $fwrite, $fdisplay.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with file output
//
// This test verifies four-state simulation with $fwrite, $fdisplay.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
integer fd;
string filename = "/tmp/verilator_xz_test.txt";
// Four-state signals
reg [3:0] a = 4'b1010;
reg [3:0] x = 4'b1X10;
reg [3:0] z = 4'bZ010;
reg [7:0] xz_data = 8'bXZ10XZ10;
initial begin
fd = $fopen(filename, "w");
if (fd == 0) begin
$display("ERROR: Could not open file %s", filename);
$finish;
end
$fwrite(fd, "=== File Output Test with X/Z ===\n");
$fwrite(fd, "a = %b (initialized)\n", a);
$fwrite(fd, "x = %b (has X)\n", x);
$fwrite(fd, "z = %b (has Z)\n", z);
$fwrite(fd, "xz_data = %b (mixed X/Z)\n", xz_data);
// Test operations with X/Z and write results
$fwrite(fd, "\n=== Operations ===\n");
$fwrite(fd, "a & x = %b\n", a & x);
$fwrite(fd, "a | z = %b\n", a | z);
$fwrite(fd, "x ^ z = %b\n", x ^ z);
$fwrite(fd, "x + z = %b\n", x + z);
// Test $fdisplay
$fwrite(fd, "\n=== Using $fdisplay ===\n");
$fdisplay(fd, "Display with x: %b", x);
$fdisplay(fd, "Display with z: %b", z);
$fdisplay(fd, "Display with xz_data: %b", xz_data);
// Test $fwrite with hex format
$fwrite(fd, "\n=== Hex Format ===\n");
$fwrite(fd, "a = %h\n", a);
$fwrite(fd, "x = %h (X becomes 0 in hex)\n", x);
$fwrite(fd, "z = %h (Z becomes 0 in hex)\n", z);
// Test uninitialized signal
reg [3:0] uninit;
$fwrite(fd, "\n=== Uninitialized Signal ===\n");
$fwrite(fd, "uninit (4-state default) = %b\n", uninit);
$fclose(fd);
$display("Wrote X/Z test output to %s", filename);
$display("Contents:");
$display("");
// Read and display the file contents
string line;
fd = $fopen(filename, "r");
while ($fgets(line, fd)) begin
$display("%s", line);
end
$fclose(fd);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X initialization with --x-sim
#
# This test verifies X initialization of four-state signals when --x-sim is enabled.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,37 @@
// DESCRIPTION: Verilator: Test X initialization with --x-sim
//
// This test verifies X initialization of four-state signals when --x-sim is enabled.
// Four-state signals should initialize to X at time 0.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t(input clk);
// Test that four-state signals initialize to X
logic [3:0] sig_4state; // Should be X at init
logic sig_bit; // Single bit should be X at init
// Counter to wait for first clock
integer count = 0;
always @(posedge clk) begin
count <= count + 1;
if (count == 0) begin
// First cycle - check initialization
// sig_4state should be XXXX (all X)
// sig_bit should be X
$write("Cycle %0d: sig_4state = %b (expect xxxx)\n", count, sig_4state);
$write("Cycle %0d: sig_bit = %b (expect x)\n", count, sig_bit);
end
else if (count == 1) begin
// After first clock, values should be assigned
$write("Cycle %0d: sig_4state = %b\n", count, sig_4state);
$write("Cycle %0d: sig_bit = %b\n", count, sig_bit);
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with larger bit widths
#
# This test verifies X and Z value propagation in 64/128/256-bit operations.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,41 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with larger bit widths (64-bit)
//
// This test verifies four-state simulation with 64-bit operations.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
// 64-bit four-state signals
reg [63:0] a64 = 64'hFEDC_BA98_7654_3210;
reg [63:0] b64 = 64'h0123_4567_89AB_CDEF;
reg [63:0] xz64 = 64'hXZ10_XZ10_XZ10_XZ10;
// Results
reg [63:0] res_and_64;
reg [63:0] res_or_64;
reg [63:0] res_xor_64;
reg [63:0] res_not_64;
initial begin
// 64-bit operations with X/Z
res_and_64 = a64 & xz64; // X & anything = X
res_or_64 = b64 | xz64; // X | anything = X
res_xor_64 = a64 ^ xz64; // XOR with X = X
res_not_64 = ~xz64; // ~X = X, ~Z = X
$write("=== 64-bit Tests ===\n");
$write("a64 = %h\n", a64);
$write("b64 = %h\n", b64);
$write("xz64 = %b\n", xz64);
$write("a64 & xz64 = %b\n", res_and_64);
$write("b64 | xz64 = %b\n", res_or_64);
$write("a64 ^ xz64 = %b\n", res_xor_64);
$write("~xz64 = %b\n", res_not_64);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with structs
#
# This test verifies X and Z value propagation in struct members.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,74 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with structs
//
// This test verifies four-state simulation with struct members.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
// Struct with four-state members
typedef struct packed {
logic [3:0] a;
logic [7:0] b;
logic flag;
} my_struct_t;
// Struct signals
my_struct_t s1 = 16'hABCD;
my_struct_t s2 = 16'h1234;
my_struct_t sx; // Uninitialized - should be X with --x-sim
my_struct_t s_result;
// Struct with X/Z values
my_struct_t sx_val;
initial begin
sx_val.a = 4'bX101;
sx_val.b = 8'bZ0101010;
sx_val.flag = 1'bX;
end
// Mixed struct operations
my_struct_t s_and;
my_struct_t s_or;
my_struct_t s_add;
initial begin
// Operations on struct members
s_and = sx & sx_val; // Uninitialized X & X = X
s_or = s1 | sx_val; // Normal | X = X
s_add = s1 + sx; // Normal + X = X
$write("=== Struct Four-State Tests ===\n");
$write("s1 = %b (initialized)\n", s1);
$write("s2 = %b (initialized)\n", s2);
$write("sx (uninitialized) = %b (expect X)\n", sx);
$write("\n=== Struct with X/Z values ===\n");
$write("sx_val.a = %b (X101)\n", sx_val.a);
$write("sx_val.b = %b (Z0101010)\n", sx_val.b);
$write("sx_val.flag = %b (X)\n", sx_val.flag);
$write("sx_val = %b\n", sx_val);
$write("\n=== Struct Operations ===\n");
$write("sx & sx_val = %b (expect all X)\n", s_and);
$write("s1 | sx_val = %b (expect X in members with X)\n", s_or);
$write("s1 + sx = %b (expect all X)\n", s_add);
// Test struct member access
$write("\n=== Struct Member Access ===\n");
$write("sx.a = %b (uninitialized member)\n", sx.a);
$write("sx.b = %b (uninitialized member)\n", sx.b);
$write("sx.flag = %b (uninitialized member)\n", sx.flag);
// Test assignment to struct with X
sx = sx_val;
$write("\n=== After Assignment ===\n");
$write("sx = %b (after sx = sx_val)\n", sx);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -0,0 +1,17 @@
#!/usr/bin/env python3
# DESCRIPTION: Verilator: Test X/Z four-state simulation with time functions
#
# This test verifies X and Z value propagation with $time, $stime, $realtime.
#
# SPDX-FileCopyrightText: 2026
# SPDX-License-Identifier: LGPL-3.0-only
import vltest_bootstrap
test.scenarios('simulator')
test.compile_extra_args = ['--x-sim']
test.execute()
test.passes()

View File

@ -0,0 +1,85 @@
// DESCRIPTION: Verilator: Test X/Z four-state simulation with time functions
//
// This test verifies four-state simulation with $time, $stime, and $realtime.
//
// SPDX-FileCopyrightText: 2026
// SPDX-License-Identifier: LGPL-3.0-only
module t;
// Four-state signals
reg [3:0] a = 4'b1010;
reg [3:0] x = 4'bXXXX;
reg [3:0] z = 4'bZZZZ;
// Variables to store time values
integer time_val;
integer stime_val;
real realtime_val;
// Test X/Z in time-related contexts
reg [7:0] result_with_x;
reg [7:0] result_with_z;
initial begin
time_val = $time;
stime_val = $stime;
realtime_val = $realtime;
$write("=== Time Function Tests ===\n");
$write("Initial $time = %0d\n", time_val);
$write("Initial $stime = %0d\n", stime_val);
$write("Initial $realtime = %0f\n", realtime_val);
// Operations with X/Z before first time increment
result_with_x = a + x; // Should propagate X
result_with_z = a | z; // Should propagate X
$write("\n=== Operations with X/Z at time 0 ===\n");
$write("a = %b (1010)\n", a);
$write("x = %b (XXXX)\n", x);
$write("z = %b (ZZZZ)\n", z);
$write("a + x = %b (expect XXXX with --x-sim)\n", result_with_x);
$write("a | z = %b (expect XXXX with --x-sim)\n", result_with_z);
#10;
time_val = $time;
stime_val = $stime;
realtime_val = $realtime;
$write("\n=== Time after #10 ===\n");
$write("$time = %0d\n", time_val);
$write("$stime = %0d\n", stime_val);
$write("$realtime = %0f\n", realtime_val);
// Operations after time advancement
result_with_x = a * x;
result_with_z = a ^ z;
$write("\n=== Operations with X/Z at time 10 ===\n");
$write("a * x = %b (expect XXXX with --x-sim)\n", result_with_x);
$write("a ^ z = %b (expect XXXX with --x-sim)\n", result_with_z);
#5.5;
time_val = $time;
realtime_val = $realtime;
$write("\n=== Time after #5.5 (time 15.5) ===\n");
$write("$time = %0d (rounded)\n", time_val);
$write("$realtime = %0f\n", realtime_val);
#100;
time_val = $time;
stime_val = $stime;
realtime_val = $realtime;
$write("\n=== Time after #100 (time 115.5) ===\n");
$write("$time = %0d\n", time_val);
$write("$stime = %0d\n", stime_val);
$write("$realtime = %0f\n", realtime_val);
$write("*-* All Finished *-*\n");
$finish;
end
endmodule