initial x/z features
This commit is contained in:
parent
af65a85a1e
commit
b9e1ca5146
|
|
@ -543,6 +543,37 @@ WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE {
|
|||
return VL_ZERO_W(obits, outwp);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Four-state reset functions - initialize to X (unknown)
|
||||
|
||||
// Set four-state value to all X (0xAAAAAAAA... in 2-bit encoding)
|
||||
static inline CData4 VL_X_RESET_4STATE_C() VL_MT_SAFE {
|
||||
return 0xAA; // 0b10101010 - X in each nibble
|
||||
}
|
||||
|
||||
static inline SData4 VL_X_RESET_4STATE_S() VL_MT_SAFE {
|
||||
return 0xAAAA; // X in each nibble
|
||||
}
|
||||
|
||||
static inline IData4 VL_X_RESET_4STATE_I() VL_MT_SAFE {
|
||||
return 0xAAAAAAAAUL; // X in each nibble
|
||||
}
|
||||
|
||||
static inline QData4 VL_X_RESET_4STATE_Q() VL_MT_SAFE {
|
||||
return 0xAAAAAAAAAAAAAAAALL; // X in each nibble
|
||||
}
|
||||
|
||||
// Wide four-state reset to X
|
||||
WDataOutP VL_X_RESET_4STATE_W(int obits, WDataOutP owp) VL_MT_SAFE {
|
||||
const int words = (obits + 31) / 32;
|
||||
for (int i = 0; i < words; ++i) owp[i] = 0xAAAAAAAAUL;
|
||||
// Mask the last word to only valid bits
|
||||
if (obits % 32) {
|
||||
owp[words - 1] &= (1UL << ((obits % 32) * 2)) - 1;
|
||||
}
|
||||
return owp;
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Debug
|
||||
|
||||
|
|
@ -1765,6 +1796,30 @@ void VL_WRITEF_NX(const std::string& format, int argc, ...) VL_MT_SAFE {
|
|||
VL_PRINTF_MT("%s", t_output.c_str());
|
||||
}
|
||||
|
||||
void VL_WRITEF_4STATE_BIN_C(const std::string& format, int lbits, CData4 data) VL_MT_SAFE {
|
||||
std::string output;
|
||||
_vl_toStringFourStateBinary_C(output, lbits, data);
|
||||
VL_PRINTF_MT("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_WRITEF_4STATE_BIN_S(const std::string& format, int lbits, SData4 data) VL_MT_SAFE {
|
||||
std::string output;
|
||||
_vl_toStringFourStateBinary_S(output, lbits, data);
|
||||
VL_PRINTF_MT("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_WRITEF_4STATE_BIN_I(const std::string& format, int lbits, IData4 data) VL_MT_SAFE {
|
||||
std::string output;
|
||||
_vl_toStringFourStateBinary_I(output, lbits, data);
|
||||
VL_PRINTF_MT("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_WRITEF_4STATE_BIN_Q(const std::string& format, int lbits, QData4 data) VL_MT_SAFE {
|
||||
std::string output;
|
||||
_vl_toStringFourStateBinary_Q(output, lbits, data);
|
||||
VL_PRINTF_MT("%s", output.c_str());
|
||||
}
|
||||
|
||||
void VL_FWRITEF_NX(IData fpi, const std::string& format, int argc, ...) VL_MT_SAFE {
|
||||
// While threadsafe, each thread can only access different file handles
|
||||
static thread_local std::string t_output; // static only for speed
|
||||
|
|
@ -2131,10 +2186,108 @@ std::string VL_TO_STRING(SData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 16, lh
|
|||
std::string VL_TO_STRING(IData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 32, lhs); }
|
||||
std::string VL_TO_STRING(QData lhs) { return VL_SFORMATF_N_NX("'h%0x", 0, 64, lhs); }
|
||||
std::string VL_TO_STRING(double lhs) { return VL_SFORMATF_N_NX("%g", 0, 64, lhs); }
|
||||
|
||||
namespace {
|
||||
char fourStateNibble(char nibble) {
|
||||
// Convert 2-bit encoding to character: 00->0, 01->1, 10->x, 11->z
|
||||
switch (nibble & 3) {
|
||||
case 0: return '0';
|
||||
case 1: return '1';
|
||||
case 2: return 'x';
|
||||
case 3: return 'z';
|
||||
default: return '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string VL_TO_STRING(CData4 lhs) {
|
||||
// Convert 4-state nibble-packed value to binary string representation
|
||||
std::string result;
|
||||
result.reserve(4);
|
||||
for (int i = 3; i >= 0; --i) {
|
||||
result += fourStateNibble((lhs >> (i * 2)) & 0x3);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string VL_TO_STRING(SData4 lhs) {
|
||||
std::string result;
|
||||
result.reserve(8);
|
||||
for (int i = 7; i >= 0; --i) {
|
||||
result += fourStateNibble((lhs >> (i * 2)) & 0x3);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string VL_TO_STRING(IData4 lhs) {
|
||||
std::string result;
|
||||
result.reserve(16);
|
||||
for (int i = 15; i >= 0; --i) {
|
||||
result += fourStateNibble((lhs >> (i * 2)) & 0x3);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string VL_TO_STRING(QData4 lhs) {
|
||||
std::string result;
|
||||
result.reserve(32);
|
||||
for (int i = 31; i >= 0; --i) {
|
||||
result += fourStateNibble((lhs >> (i * 2)) & 0x3);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
std::string VL_TO_STRING_W(int words, const WDataInP obj) {
|
||||
return VL_SFORMATF_N_NX("'h%0x", 0, words * VL_EDATASIZE, obj);
|
||||
}
|
||||
|
||||
//===========================================================================
|
||||
// Four-state to string helpers for $display
|
||||
|
||||
static inline void _vl_toStringFourStateBinary_C(std::string& output, int lbits, CData4 ld) {
|
||||
for (int i = lbits - 1; i >= 0; --i) {
|
||||
const uint8_t val = (ld >> (i * 2)) & 3;
|
||||
switch (val) {
|
||||
case 0: output += '0'; break;
|
||||
case 1: output += '1'; break;
|
||||
case 2: output += 'x'; break;
|
||||
case 3: output += 'z'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _vl_toStringFourStateBinary_S(std::string& output, int lbits, SData4 ld) {
|
||||
for (int i = lbits - 1; i >= 0; --i) {
|
||||
const uint8_t val = (ld >> (i * 2)) & 3;
|
||||
switch (val) {
|
||||
case 0: output += '0'; break;
|
||||
case 1: output += '1'; break;
|
||||
case 2: output += 'x'; break;
|
||||
case 3: output += 'z'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _vl_toStringFourStateBinary_I(std::string& output, int lbits, IData4 ld) {
|
||||
for (int i = lbits - 1; i >= 0; --i) {
|
||||
const uint8_t val = (ld >> (i * 2)) & 3;
|
||||
switch (val) {
|
||||
case 0: output += '0'; break;
|
||||
case 1: output += '1'; break;
|
||||
case 2: output += 'x'; break;
|
||||
case 3: output += 'z'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static inline void _vl_toStringFourStateBinary_Q(std::string& output, int lbits, QData4 ld) {
|
||||
for (int i = lbits - 1; i >= 0; --i) {
|
||||
const uint8_t val = (ld >> (i * 2)) & 3;
|
||||
switch (val) {
|
||||
case 0: output += '0'; break;
|
||||
case 1: output += '1'; break;
|
||||
case 2: output += 'x'; break;
|
||||
case 3: output += 'z'; break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string VL_TOLOWER_NN(const std::string& ld) VL_PURE {
|
||||
std::string result = ld;
|
||||
for (auto& cr : result) cr = std::tolower(cr);
|
||||
|
|
|
|||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,13 @@ extern WDataOutP VL_RAND_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
|
|||
/// Zero reset a signal (slow - else use VL_ZERO_W)
|
||||
extern WDataOutP VL_ZERO_RESET_W(int obits, WDataOutP outwp) VL_MT_SAFE;
|
||||
|
||||
/// Four-state reset - initialize to X (unknown)
|
||||
static inline CData4 VL_X_RESET_4STATE_C() VL_MT_SAFE;
|
||||
static inline SData4 VL_X_RESET_4STATE_S() VL_MT_SAFE;
|
||||
static inline IData4 VL_X_RESET_4STATE_I() VL_MT_SAFE;
|
||||
static inline QData4 VL_X_RESET_4STATE_Q() VL_MT_SAFE;
|
||||
extern WDataOutP VL_X_RESET_4STATE_W(int obits, WDataOutP owp) VL_MT_SAFE;
|
||||
|
||||
extern void VL_PRINTTIMESCALE(const char* namep, const char* timeunitp,
|
||||
const VerilatedContext* contextp) VL_MT_SAFE;
|
||||
|
||||
|
|
@ -154,6 +161,12 @@ 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;
|
||||
|
||||
// Four-state display functions - output X/Z for four-state values
|
||||
extern void VL_WRITEF_4STATE_BIN_C(const std::string& format, int lbits, CData4 data) VL_MT_SAFE;
|
||||
extern void VL_WRITEF_4STATE_BIN_S(const std::string& format, int lbits, SData4 data) VL_MT_SAFE;
|
||||
extern void VL_WRITEF_4STATE_BIN_I(const std::string& format, int lbits, IData4 data) VL_MT_SAFE;
|
||||
extern void VL_WRITEF_4STATE_BIN_Q(const std::string& format, int lbits, 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;
|
||||
|
|
@ -897,6 +910,276 @@ static inline WDataOutP VL_NOT_W(int words, WDataOutP owp, WDataInP const lwp) V
|
|||
return owp;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FOUR-STATE LOGICAL OPERATORS (X/Z support)
|
||||
// For four-state: 00=0, 01=1, 10=X, 11=Z
|
||||
|
||||
// Four-state AND: X & anything = X, Z & anything = X, 0 & anything = 0, 1 & anything = anything
|
||||
static inline uint8_t VL_AND_4STATE(uint8_t lhs, uint8_t rhs) {
|
||||
const uint8_t lval = lhs & 3;
|
||||
const uint8_t rval = rhs & 3;
|
||||
// X & anything = X
|
||||
if (lval == 2 || rval == 2) return 2; // X
|
||||
// Z & anything = X
|
||||
if (lval == 3 || rval == 3) return 2; // X
|
||||
// 0 & anything = 0
|
||||
if (lval == 0 || rval == 0) return 0; // 0
|
||||
// 1 & anything = anything
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Four-state OR
|
||||
static inline uint8_t VL_OR_4STATE(uint8_t lhs, uint8_t rhs) {
|
||||
const uint8_t lval = lhs & 3;
|
||||
const uint8_t rval = rhs & 3;
|
||||
// X | anything = X
|
||||
if (lval == 2 || rval == 2) return 2; // X
|
||||
// Z | anything = X
|
||||
if (lval == 3 || rval == 3) return 2; // X
|
||||
// 1 | anything = 1
|
||||
if (lval == 1 || rval == 1) return 1; // 1
|
||||
// 0 | anything = anything
|
||||
return rval;
|
||||
}
|
||||
|
||||
// Four-state XOR
|
||||
static inline uint8_t VL_XOR_4STATE(uint8_t lhs, uint8_t rhs) {
|
||||
const uint8_t lval = lhs & 3;
|
||||
const uint8_t rval = rhs & 3;
|
||||
// X ^ anything = X
|
||||
if (lval == 2 || rval == 2) return 2; // X
|
||||
// Z ^ anything = X
|
||||
if (lval == 3 || rval == 3) return 2; // X
|
||||
// Otherwise XOR the clean values
|
||||
return (lval ^ rval);
|
||||
}
|
||||
|
||||
// Four-state NOT
|
||||
static inline uint8_t VL_NOT_4STATE(uint8_t lhs) {
|
||||
const uint8_t lval = lhs & 3;
|
||||
if (lval == 2) return 2; // X -> X
|
||||
if (lval == 3) return 2; // Z -> X
|
||||
return lval ^ 1; // 0 -> 1, 1 -> 0
|
||||
}
|
||||
|
||||
// Four-state byte operations
|
||||
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 res = VL_AND_4STATE(lb, rb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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 res = VL_OR_4STATE(lb, rb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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 res = VL_XOR_4STATE(lb, rb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
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 res = VL_NOT_4STATE(lb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Four-state SData (8-bit) operations
|
||||
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 res = VL_AND_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_OR_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_XOR_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_NOT_4STATE(lb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Four-state IData (16-bit) operations
|
||||
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 res = VL_AND_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_OR_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_XOR_4STATE(lb, rb);
|
||||
result |= (res << (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 res = VL_NOT_4STATE(lb);
|
||||
result |= (res << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
// Four-state QData (32-bit) operations
|
||||
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 res = VL_AND_4STATE(lb, rb);
|
||||
result |= (static_cast<QData4>(res) << (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 res = VL_OR_4STATE(lb, rb);
|
||||
result |= (static_cast<QData4>(res) << (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 res = VL_XOR_4STATE(lb, rb);
|
||||
result |= (static_cast<QData4>(res) << (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 res = VL_NOT_4STATE(lb);
|
||||
result |= (static_cast<QData4>(res) << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FOUR-STATE COMPARISONS
|
||||
// For four-state: any X or Z in comparison returns X (unknown)
|
||||
|
||||
// Four-state EQ: returns true if equal and both operands are deterministic
|
||||
static inline bool VL_EQ_4STATE_C(CData4 lhs, CData4 rhs) {
|
||||
if (_vl4_anyXZ_C(lhs) || _vl4_anyXZ_C(rhs)) return false;
|
||||
return (lhs & 0x55555555) == (rhs & 0x55555555); // Mask to get lower bit only
|
||||
}
|
||||
|
||||
static inline bool VL_EQ_4STATE_S(SData4 lhs, SData4 rhs) {
|
||||
if (_vl4_anyXZ_S(lhs) || _vl4_anyXZ_S(rhs)) return false;
|
||||
return (lhs & 0x5555555555555555ULL) == (rhs & 0x5555555555555555ULL);
|
||||
}
|
||||
|
||||
static inline bool VL_EQ_4STATE_I(IData4 lhs, IData4 rhs) {
|
||||
if (_vl4_anyXZ_I(lhs) || _vl4_anyXZ_I(rhs)) return false;
|
||||
return (lhs & 0x5555555555555555ULL) == (rhs & 0x5555555555555555ULL);
|
||||
}
|
||||
|
||||
static inline bool VL_EQ_4STATE_Q(QData4 lhs, QData4 rhs) {
|
||||
if (_vl4_anyXZ_Q(lhs) || _vl4_anyXZ_Q(rhs)) return false;
|
||||
return (lhs & 0x5555555555555555ULL) == (rhs & 0x5555555555555555ULL);
|
||||
}
|
||||
|
||||
// Four-state NEQ
|
||||
static inline bool VL_NEQ_4STATE_C(CData4 lhs, CData4 rhs) {
|
||||
return !VL_EQ_4STATE_C(lhs, rhs);
|
||||
}
|
||||
|
||||
static inline bool VL_NEQ_4STATE_S(SData4 lhs, SData4 rhs) {
|
||||
return !VL_EQ_4STATE_S(lhs, rhs);
|
||||
}
|
||||
|
||||
static inline bool VL_NEQ_4STATE_I(IData4 lhs, IData4 rhs) {
|
||||
return !VL_EQ_4STATE_I(lhs, rhs);
|
||||
}
|
||||
|
||||
static inline bool VL_NEQ_4STATE_Q(QData4 lhs, QData4 rhs) {
|
||||
return !VL_EQ_4STATE_Q(lhs, rhs);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// Logical comparisons
|
||||
|
||||
|
|
@ -1204,6 +1487,195 @@ static inline WDataOutP VL_MODDIVS_WWW(int lbits, WDataOutP owp, WDataInP const
|
|||
}
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FOUR-STATE ARITHMETIC OPERATORS
|
||||
// For four-state: any X or Z in operands results in X output
|
||||
|
||||
// Helper: Check if a four-state nibble has X or Z
|
||||
static inline bool _vl4_isXZ(uint8_t val) {
|
||||
return (val & 3) >= 2; // 2=X, 3=Z
|
||||
}
|
||||
|
||||
// Helper: Check if any bit in a four-state value is X or Z
|
||||
static inline bool _vl4_anyXZ_C(CData4 val) {
|
||||
for (int i = 0; i < 4; i++) {
|
||||
if (_vl4_isXZ((val >> (i * 2)) & 3)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool _vl4_anyXZ_S(SData4 val) {
|
||||
for (int i = 0; i < 8; i++) {
|
||||
if (_vl4_isXZ((val >> (i * 2)) & 3)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool _vl4_anyXZ_I(IData4 val) {
|
||||
for (int i = 0; i < 16; i++) {
|
||||
if (_vl4_isXZ((val >> (i * 2)) & 3)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static inline bool _vl4_anyXZ_Q(QData4 val) {
|
||||
for (int i = 0; i < 32; i++) {
|
||||
if (_vl4_isXZ((val >> (i * 2)) & 3)) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// 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 0xAAAAAAAA; // All X (2 in each nibble = 0b10101010)
|
||||
}
|
||||
// Extract clean values and add
|
||||
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 0xAAAAAAAAAAAAAAAALL; // All X
|
||||
}
|
||||
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 0xAAAAAAAAAAAAAAAALL; // All X
|
||||
}
|
||||
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 0xAAAAAAAAAAAAAAAALL; // All X
|
||||
}
|
||||
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
|
||||
static inline CData4 VL_SUB_4STATE_C(CData4 lhs, CData4 rhs) {
|
||||
if (_vl4_anyXZ_C(lhs) || _vl4_anyXZ_C(rhs)) {
|
||||
return 0xAAAAAAAA; // All X
|
||||
}
|
||||
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;
|
||||
if (diff < 0) {
|
||||
diff += 2;
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
result |= (static_cast<CData4>(diff & 1) << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline SData4 VL_SUB_4STATE_S(SData4 lhs, SData4 rhs) {
|
||||
if (_vl4_anyXZ_S(lhs) || _vl4_anyXZ_S(rhs)) {
|
||||
return 0xAAAAAAAAAAAAAAAALL;
|
||||
}
|
||||
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;
|
||||
if (diff < 0) {
|
||||
diff += 2;
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
result |= (static_cast<SData4>(diff & 1) << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline IData4 VL_SUB_4STATE_I(IData4 lhs, IData4 rhs) {
|
||||
if (_vl4_anyXZ_I(lhs) || _vl4_anyXZ_I(rhs)) {
|
||||
return 0xAAAAAAAAAAAAAAAALL;
|
||||
}
|
||||
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;
|
||||
if (diff < 0) {
|
||||
diff += 2;
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
result |= (static_cast<IData4>(diff & 1) << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static inline QData4 VL_SUB_4STATE_Q(QData4 lhs, QData4 rhs) {
|
||||
if (_vl4_anyXZ_Q(lhs) || _vl4_anyXZ_Q(rhs)) {
|
||||
return 0xAAAAAAAAAAAAAAAALL;
|
||||
}
|
||||
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;
|
||||
if (diff < 0) {
|
||||
diff += 2;
|
||||
borrow = 1;
|
||||
} else {
|
||||
borrow = 0;
|
||||
}
|
||||
result |= (static_cast<QData4>(diff & 1) << (i * 2));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
#define VL_POW_IIQ(obits, lbits, rbits, lhs, rhs) VL_POW_QQQ(obits, lbits, rbits, lhs, rhs)
|
||||
#define VL_POW_IIW(obits, lbits, rbits, lhs, rwp) VL_POW_QQW(obits, lbits, rbits, lhs, rwp)
|
||||
#define VL_POW_QQI(obits, lbits, rbits, lhs, rhs) VL_POW_QQQ(obits, lbits, rbits, lhs, rhs)
|
||||
|
|
@ -2167,6 +2639,134 @@ static inline QData VL_SHIFTRS_QQQ(int obits, int lbits, int rbits, QData lhs, Q
|
|||
return VL_SHIFTRS_QQW(obits, lbits, rbits, lhs, rwp);
|
||||
}
|
||||
|
||||
//=========================================================================
|
||||
// FOUR-STATE SHIFT OPERATORS
|
||||
// For four-state: shift operations preserve X/Z in the shifted bits
|
||||
|
||||
// Four-state left shift: shift in zeros, preserve X/Z pattern
|
||||
static inline CData4 VL_SHIFTL_4STATE_C(CData4 lhs, int shift) {
|
||||
if (shift >= 4) return 0; // All shifted out
|
||||
if (_vl4_anyXZ_C(lhs)) {
|
||||
// X/Z gets shifted, lower bits become 0
|
||||
CData4 result = 0;
|
||||
for (int i = 0; i < 4 - shift; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (val << ((i + shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
// Clean value shift
|
||||
return (lhs & 0x55555555) << shift;
|
||||
}
|
||||
|
||||
static inline SData4 VL_SHIFTL_4STATE_S(SData4 lhs, int shift) {
|
||||
if (shift >= 8) return 0;
|
||||
if (_vl4_anyXZ_S(lhs)) {
|
||||
SData4 result = 0;
|
||||
for (int i = 0; i < 8 - shift; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<SData4>(val) << ((i + shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) << shift;
|
||||
}
|
||||
|
||||
static inline IData4 VL_SHIFTL_4STATE_I(IData4 lhs, int shift) {
|
||||
if (shift >= 16) return 0;
|
||||
if (_vl4_anyXZ_I(lhs)) {
|
||||
IData4 result = 0;
|
||||
for (int i = 0; i < 16 - shift; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<IData4>(val) << ((i + shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) << shift;
|
||||
}
|
||||
|
||||
static inline QData4 VL_SHIFTL_4STATE_Q(QData4 lhs, int shift) {
|
||||
if (shift >= 32) return 0;
|
||||
if (_vl4_anyXZ_Q(lhs)) {
|
||||
QData4 result = 0;
|
||||
for (int i = 0; i < 32 - shift; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<QData4>(val) << ((i + shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) << shift;
|
||||
}
|
||||
|
||||
// Four-state right shift
|
||||
static inline CData4 VL_SHIFTR_4STATE_C(CData4 lhs, int shift) {
|
||||
if (shift >= 4) return 0;
|
||||
if (_vl4_anyXZ_C(lhs)) {
|
||||
CData4 result = 0;
|
||||
for (int i = shift; i < 4; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<CData4>(val) << ((i - shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x55555555) >> shift;
|
||||
}
|
||||
|
||||
static inline SData4 VL_SHIFTR_4STATE_S(SData4 lhs, int shift) {
|
||||
if (shift >= 8) return 0;
|
||||
if (_vl4_anyXZ_S(lhs)) {
|
||||
SData4 result = 0;
|
||||
for (int i = shift; i < 8; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<SData4>(val) << ((i - shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) >> shift;
|
||||
}
|
||||
|
||||
static inline IData4 VL_SHIFTR_4STATE_I(IData4 lhs, int shift) {
|
||||
if (shift >= 16) return 0;
|
||||
if (_vl4_anyXZ_I(lhs)) {
|
||||
IData4 result = 0;
|
||||
for (int i = shift; i < 16; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<IData4>(val) << ((i - shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) >> shift;
|
||||
}
|
||||
|
||||
static inline QData4 VL_SHIFTR_4STATE_Q(QData4 lhs, int shift) {
|
||||
if (shift >= 32) return 0;
|
||||
if (_vl4_anyXZ_Q(lhs)) {
|
||||
QData4 result = 0;
|
||||
for (int i = shift; i < 32; i++) {
|
||||
uint8_t val = (lhs >> (i * 2)) & 3;
|
||||
if (val != 0) {
|
||||
result |= (static_cast<QData4>(val) << ((i - shift) * 2));
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
return (lhs & 0x5555555555555555ULL) >> shift;
|
||||
}
|
||||
|
||||
//===================================================================
|
||||
// Bit selection
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -278,6 +278,26 @@ 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
|
||||
if (argp->dtypep()->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";
|
||||
}
|
||||
m_emitDispState.pushArg(' ', argp, func);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
// string pfmt = "%"+displayFormat(argp, vfmt, fmtLetter)+fmtLetter;
|
||||
string pfmt;
|
||||
|
|
@ -684,6 +704,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 +716,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("
|
||||
|
|
@ -722,6 +749,19 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, bool constructing,
|
|||
UASSERT_OBJ(constp, varp, "non-const initializer for variable");
|
||||
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";
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -1947,6 +1947,8 @@ 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,
|
||||
"Enable four-state simulation with X/Z support");
|
||||
|
||||
DECL_OPTION("-y", CbVal, [this, &optdir](const char* valp) {
|
||||
addIncDirUser(parseFileArg(optdir, string{valp}));
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
@ -589,6 +590,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(); }
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
|
|
@ -0,0 +1,64 @@
|
|||
// 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
|
||||
|
||||
module t(input clk);
|
||||
|
||||
logic [3:0] a;
|
||||
logic [3:0] b;
|
||||
logic [3:0] y_and;
|
||||
logic [3:0] y_or;
|
||||
logic [3:0] y_xor;
|
||||
logic [3:0] y_add;
|
||||
logic [3:0] y_sub;
|
||||
logic y_eq;
|
||||
logic y_neq;
|
||||
|
||||
// Test X propagation through logical operations
|
||||
always @(posedge clk) begin
|
||||
a <= 4'b1010;
|
||||
b <= 4'b01xz; // Contains X and Z
|
||||
end
|
||||
|
||||
// AND: X & anything = X, Z & anything = X
|
||||
assign y_and = a & b;
|
||||
|
||||
// OR
|
||||
assign y_or = a | b;
|
||||
|
||||
// XOR
|
||||
assign y_xor = a ^ b;
|
||||
|
||||
// Addition: X + anything = X
|
||||
assign y_add = a + b;
|
||||
|
||||
// Subtraction
|
||||
assign y_sub = a - b;
|
||||
|
||||
// Comparisons with X return false (for !==)
|
||||
assign y_eq = (a == b);
|
||||
assign y_neq = (a != b);
|
||||
|
||||
// Check results
|
||||
always @(posedge clk) begin
|
||||
// With --x-sim, b has X/Z, so results should propagate X
|
||||
// We just verify the simulator runs without crashing
|
||||
if (a == 4'b1010) begin
|
||||
$write("a = %b (expected 1010)\n", a);
|
||||
$write("b = %b (expected 01xz)\n", b);
|
||||
$write("a & b = %b\n", y_and);
|
||||
$write("a | b = %b\n", y_or);
|
||||
$write("a ^ b = %b\n", y_xor);
|
||||
$write("a + b = %b\n", y_add);
|
||||
$write("a - b = %b\n", y_sub);
|
||||
$write("a == b = %b (should be 0 or x due to X)\n", y_eq);
|
||||
$write("a != b = %b (should be 1 or x due to X)\n", y_neq);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
|
||||
endmodule
|
||||
|
|
@ -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()
|
||||
|
|
@ -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
|
||||
Loading…
Reference in New Issue