Merge branch 'master' into develop-v5

This commit is contained in:
Wilson Snyder 2022-09-20 20:06:21 -04:00
commit d162619bd3
28 changed files with 837 additions and 301 deletions

View File

@ -34,6 +34,7 @@ Verilator 4.227 devel
* Support IEEE constant signal strengths (#3601). [Ryszard Rozak/Antmicro]
* Add --main to generate main() C++ (previously was experimental only).
* Rename --bin to --build-dep-bin.
* Fix thread saftey in SystemC VL_ASSIGN_SBW/WSB (#3494) (#3513). [Mladen Slijepcevic]
* Fix crash in gate optimization of circular logic (#3543). [Bill Flynn]
* Fix arguments in non-static method call (#3547) (#3582). [Gustav Svensk]

View File

@ -282,8 +282,8 @@ detailed descriptions of these arguments.
--autoflush Flush streams after all $displays
--bbox-sys Blackbox unknown $system calls
--bbox-unsup Blackbox unsupported language features
--bin <filename> Override Verilator binary
--build Build model executable/library after Verilation
--build-dep-bin <filename> Override build dependency Verilator binary
--build-jobs <jobs> Parallelism for --build
--cc Create C++ output
--cdc Clock domain crossing analysis

View File

@ -130,5 +130,6 @@ Yoda Lee
Yossi Nivin
Yuri Victorovich
Yutetsu TAKATSUKASA
Yu-Sheng Lin
Yves Mathieu
Zhanglei Wang

View File

@ -115,13 +115,6 @@ Summary:
Using this argument will likely cause incorrect simulation.
.. option:: --bin <filename>
Rarely needed. Override the default filename for Verilator itself.
When a dependency (.d) file is created, this filename will become a
source dependency, such that a change in this binary will have make
rebuild the output files.
.. option:: --build
After generating the SystemC/C++ code, Verilator will invoke the
@ -129,6 +122,15 @@ Summary:
is also used). Verilator manages the build itself, and for this --build
requires GNU Make to be available on the platform.
.. option:: --build-dep-bin <filename>
Rarely needed. When a dependency (.d) file is created, this filename
will become a source dependency, such that a change in this binary will
have make rebuild the output files. Defaults to the full path to the
Verilator binary.
This option was named `--bin` prior to version 4.228.
.. option:: --build-jobs [<value>]
Specify the level of parallelism for :vlopt:`--build`. If zero, uses the

View File

@ -563,6 +563,7 @@ template <class T>
T const& as_const(T& v) {
return v;
}
}; // namespace vlstd
//=========================================================================

View File

@ -542,6 +542,11 @@ class EmitCModel final : public EmitCFunc {
"elaboration.\");\n");
puts(/**/ "}");
}
puts(/**/ "if (tfp->isOpen()) {\n");
puts(/****/ "vl_fatal(__FILE__, __LINE__, __FILE__,\"'" + topClassName()
+ +"::trace()' shall not be called after '" + v3Global.opt.traceClassBase()
+ "C::open()'.\");\n");
puts(/**/ "}\n");
puts(/**/ "if (false && levels && options) {} // Prevent unused\n");
puts(/**/ "tfp->spTrace()->addModel(this);\n");
puts(/**/ "tfp->spTrace()->addInitCb(&" + protect("trace_init") + ", &(vlSymsp->TOP));\n");

View File

@ -297,7 +297,7 @@ class EmitMkHierVerilation final {
of.puts("# Verilation of hierarchical blocks are executed in this directory\n");
of.puts("VM_HIER_RUN_DIR := " + cwd + "\n");
of.puts("# Common options for hierarchical blocks\n");
const string fullpath_bin = V3Os::filenameRealPath(v3Global.opt.bin());
const string fullpath_bin = V3Os::filenameRealPath(v3Global.opt.buildDepBin());
const string verilator_wrapper = V3Os::filenameDir(fullpath_bin) + "/verilator";
of.puts("VM_HIER_VERILATOR := " + verilator_wrapper + "\n");
of.puts("VM_HIER_INPUT_FILES := \\\n");

View File

@ -154,7 +154,7 @@ void V3FileDependImp::writeDepend(const string& filename) {
if (i.target()) *ofp << i.filename() << " ";
}
*ofp << " : ";
*ofp << v3Global.opt.bin();
*ofp << v3Global.opt.buildDepBin();
*ofp << " ";
for (const DependFile& i : m_filenameList) {

View File

@ -44,7 +44,7 @@ constexpr int MAX_SPRINTF_DOUBLE_SIZE
"Number operation called with same source and dest")
#define NUM_ASSERT_LOGIC_ARGS1(arg1) \
UASSERT((!(arg1).isDouble() && !(arg1).isString()), \
UASSERT(((arg1).dataType() == V3NumberData::V3NumberDataType::LOGIC), \
"Number operation called with non-logic (double or string) argument: '" << (arg1) \
<< '"')
#define NUM_ASSERT_LOGIC_ARGS2(arg1, arg2) \
@ -79,8 +79,10 @@ void V3Number::v3errorEnd(std::ostringstream& str) const {
nsstr << str.str();
if (m_nodep) {
m_nodep->v3errorEnd(nsstr);
} else {
} else if (m_fileline) {
m_fileline->v3errorEnd(nsstr);
} else {
V3Error::v3errorEnd(nsstr);
}
}
@ -96,13 +98,11 @@ void V3Number::v3errorEndFatal(std::ostringstream& str) const {
V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) {
// Create a number using a verilog string as the value, thus 8 bits per character.
// cppcheck bug - doesn't see init() resets these
// cppcheck: Member variable 'm_sized/m_width' is not initialized in the constructor
init(nodep, str.length() * 8);
m_fromString = true;
init(nodep, std::max<int>(str.length() * 8, 1));
m_data.m_fromString = true;
for (unsigned pos = 0; pos < str.length(); ++pos) {
const int topos = str.length() - 1 - pos;
ValueAndX& v = m_value[topos / 4];
ValueAndX& v = m_data.num()[topos / 4];
for (int bit = 0; bit < 8; ++bit) {
if (str[pos] & (1UL << bit)) { v.m_value |= (1UL << (bit + (topos % 4) * 8)); }
}
@ -112,7 +112,7 @@ V3Number::V3Number(VerilogStringLiteral, AstNode* nodep, const string& str) {
V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) {
if (nodedtypep->isString()) {
init(nodep, 0);
init(nodep);
setString("");
} else if (nodedtypep->isDouble()) {
init(nodep, 64);
@ -124,6 +124,7 @@ V3Number::V3Number(AstNode* nodep, const AstNodeDType* nodedtypep) {
void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl) {
init(nodep, 0);
m_data.setLogic();
m_fileline = fl;
const char* value_startp = sourcep;
for (const char* cp = sourcep; *cp; cp++) {
@ -172,35 +173,35 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl)
base = 'd';
}
for (int i = 0; i < words(); ++i) m_value[i] = {0, 0};
for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0};
// Special SystemVerilog unsized constructs
if (base == '0') {
width(1, false); // So we extend it
setBit(0, 0);
width(1, false); // So we extend it
m_autoExtend = true;
m_data.m_autoExtend = true;
} else if (base == '1') {
width(1, false); // So we extend it
setBit(0, 1);
width(1, false); // So we extend it
m_autoExtend = true;
m_data.m_autoExtend = true;
} else if (tolower(base) == 'z') {
width(1, false); // So we extend it
setBit(0, 'z');
width(1, false); // So we extend it
m_autoExtend = true;
m_data.m_autoExtend = true;
} else if (tolower(base) == 'x') {
setBit(0, 'x');
width(1, false); // So we extend it
m_autoExtend = true;
setBit(0, 'x');
m_data.m_autoExtend = true;
}
// Otherwise...
else if (!m_sized) {
else if (!sized()) {
width(32, false); // Says IEEE 1800-2012 5.7.1
if (unbased) isSigned(true); // Also says the spec.
}
// Ignore leading blanks
while (*value_startp == '_' || isspace(*value_startp)) value_startp++;
if (!*value_startp && !m_autoExtend) {
if (!*value_startp && !m_data.m_autoExtend) {
v3error("Number is missing value digits: " << sourcep);
}
@ -229,7 +230,7 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl)
if (olen <= 7) { // 10000000 fits in 32 bits, so ok
// Constants are common, so for speed avoid wide math until we need it
val = val * 10 + (*cp - '0');
m_value[0].m_value = val;
m_data.num()[0].m_value = val;
} else { // Wide; all previous digits are already in m_value[0]
// this = (this * 10)/*product*/ + (*cp-'0')/*addend*/
// Assumed rare; lots of optimizations are possible here
@ -237,12 +238,12 @@ void V3Number::V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl)
const V3Number ten(this, width() + 4, 10);
const V3Number addend(this, width(), (*cp - '0'));
product.opMul(*this, ten);
this->opAdd(product, addend);
opAdd(product, addend);
if (product.bitsValue(width(), 4)) { // Overflowed
static int warned = 0;
v3error("Too many digits for "
<< width() << " bit number: " << sourcep << '\n'
<< ((!m_sized && !warned++) ? (
<< ((!sized() && !warned++) ? (
V3Error::warnMore() + "... As that number was unsized"
+ " ('d...) it is limited to 32 bits (IEEE 1800-2017 "
"5.7.1)\n"
@ -387,72 +388,72 @@ int V3Number::log2b(uint32_t num) {
// Setters
V3Number& V3Number::setZero() {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0};
return *this;
}
V3Number& V3Number::setQuad(uint64_t value) {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
m_value[0].m_value = value & 0xffffffffULL;
if (width() > 32) m_value[1].m_value = (value >> 32ULL) & 0xffffffffULL;
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0};
m_data.num()[0].m_value = value & 0xffffffffULL;
if (width() > 32) m_data.num()[1].m_value = (value >> 32ULL) & 0xffffffffULL;
opCleanThis();
return *this;
}
V3Number& V3Number::setLong(uint32_t value) {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
m_value[0].m_value = value;
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0};
m_data.num()[0].m_value = value;
opCleanThis();
return *this;
}
V3Number& V3Number::setLongS(int32_t value) {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0};
union {
uint32_t u;
int32_t s;
} u;
u.s = value;
if (u.s) {}
m_value[0].m_value = u.u;
m_data.num()[0].m_value = u.u;
opCleanThis();
return *this;
}
V3Number& V3Number::setDouble(double value) {
if (VL_UNCOVERABLE(width() != 64)) v3fatalSrc("Real operation on wrong sized number");
m_double = true;
m_data.setDouble();
union {
double d;
uint32_t u[2];
} u;
u.d = value;
if (u.d != 0.0) {}
for (int i = 2; i < words(); i++) m_value[i] = {0, 0};
m_value[0].m_value = u.u[0];
m_value[1].m_value = u.u[1];
for (int i = 2; i < words(); i++) m_data.num()[i] = {0, 0};
m_data.num()[0].m_value = u.u[0];
m_data.num()[1].m_value = u.u[1];
return *this;
}
V3Number& V3Number::setSingleBits(char value) {
for (int i = 1 /*upper*/; i < words(); i++) m_value[i] = {0, 0};
m_value[0] = {(value == '1' || value == 'x' || value == 1 || value == 3),
(value == 'z' || value == 'x' || value == 2 || value == 3)};
for (int i = 1 /*upper*/; i < words(); i++) m_data.num()[i] = {0, 0};
m_data.num()[0] = {(value == '1' || value == 'x' || value == 1 || value == 3),
(value == 'z' || value == 'x' || value == 2 || value == 3)};
return *this;
}
V3Number& V3Number::setAllBits0() {
for (int i = 0; i < words(); i++) m_value[i] = {0, 0};
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, 0};
return *this;
}
V3Number& V3Number::setAllBits1() {
for (int i = 0; i < words(); i++) m_value[i] = {~0U, 0};
for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, 0};
opCleanThis();
return *this;
}
V3Number& V3Number::setAllBitsX() {
// Use setAllBitsXRemoved if calling this based on a non-X/Z input value such as divide by zero
for (int i = 0; i < words(); i++) m_value[i] = {~0U, ~0U};
for (int i = 0; i < words(); i++) m_data.num()[i] = {~0U, ~0U};
opCleanThis();
return *this;
}
V3Number& V3Number::setAllBitsZ() {
for (int i = 0; i < words(); i++) m_value[i] = {0, ~0U};
for (int i = 0; i < words(); i++) m_data.num()[i] = {0, ~0U};
opCleanThis();
return *this;
}
@ -493,7 +494,7 @@ string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
} else if (isString()) {
return '"' + toString() + '"';
} else {
if (VL_UNCOVERABLE((m_value[words() - 1].m_value | m_value[words() - 1].m_valueX)
if (VL_UNCOVERABLE((m_data.num()[words() - 1].m_value | m_data.num()[words() - 1].m_valueX)
& ~hiWordMask())) {
out << "%E-hidden-bits"; // LCOV_EXCL_LINE
}
@ -688,7 +689,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
}
case 's': {
// Spec says always drop leading zeros, this isn't quite right, we space pad.
int bit = this->width() - 1;
int bit = width() - 1;
bool start = true;
while ((bit % 8) != 7) bit++;
for (; bit >= 0; bit -= 8) {
@ -709,7 +710,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
case 'd': { // Unsigned decimal
const bool issigned = (code == '~');
if (fmtsize == "") {
const double mantissabits = this->width() - (issigned ? 1 : 0);
const double mantissabits = width() - (issigned ? 1 : 0);
// To get the number of digits required, we want to compute
// log10(2**mantissabits) and round it up. To be able to handle
// a very wide mantissa, we use log2(2**mantissabits)/log2(10),
@ -765,7 +766,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
// 'p' // Packed - converted to another code by V3Width
case 'u': { // Packed 2-state
for (int i = 0; i < words(); i++) {
const uint32_t v = m_value[i].m_value;
const uint32_t v = m_data.num()[i].m_value;
str += static_cast<char>((v >> 0) & 0xff);
str += static_cast<char>((v >> 8) & 0xff);
str += static_cast<char>((v >> 16) & 0xff);
@ -775,7 +776,7 @@ string V3Number::displayed(FileLine* fl, const string& vformat) const {
}
case 'z': { // Packed 4-state
for (int i = 0; i < words(); i++) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
str += static_cast<char>((v.m_value >> 0) & 0xff);
str += static_cast<char>((v.m_value >> 8) & 0xff);
str += static_cast<char>((v.m_value >> 16) & 0xff);
@ -872,12 +873,12 @@ uint32_t V3Number::toUInt() const {
UASSERT(!isFourState(), "toUInt with 4-state " << *this);
// We allow wide numbers that represent values <= 32 bits
for (int i = 1; i < words(); ++i) {
if (m_value[i].m_value) {
if (m_data.num()[i].m_value) {
v3error("Value too wide for 32-bits expected in this context " << *this);
break;
}
}
return m_value[0].m_value;
return m_data.num()[0].m_value;
}
double V3Number::toDouble() const {
@ -888,8 +889,8 @@ double V3Number::toDouble() const {
double d;
uint32_t u[2];
} u;
u.u[0] = m_value[0].m_value;
u.u[1] = m_value[1].m_value;
u.u[0] = m_data.num()[0].m_value;
u.u[1] = m_data.num()[1].m_value;
return u.d;
}
@ -911,14 +912,14 @@ uint64_t V3Number::toUQuad() const {
// We allow wide numbers that represent values <= 64 bits
if (isDouble()) return static_cast<uint64_t>(toDouble());
for (int i = 2; i < words(); ++i) {
if (m_value[i].m_value) {
if (m_data.num()[i].m_value) {
v3error("Value too wide for 64-bits expected in this context " << *this);
break;
}
}
if (width() <= 32) return (static_cast<uint64_t>(toUInt()));
return ((static_cast<uint64_t>(m_value[1].m_value) << 32ULL)
| (static_cast<uint64_t>(m_value[0].m_value)));
return ((static_cast<uint64_t>(m_data.num()[1].m_value) << 32ULL)
| (static_cast<uint64_t>(m_data.num()[0].m_value)));
}
int64_t V3Number::toSQuad() const {
@ -932,8 +933,8 @@ int64_t V3Number::toSQuad() const {
string V3Number::toString() const {
UASSERT(!isFourState(), "toString with 4-state " << *this);
// Spec says always drop leading zeros, this isn't quite right, we space pad.
if (isString()) return m_stringVal;
int bit = this->width() - 1;
if (isString()) return m_data.str();
int bit = width() - 1;
bool start = true;
while ((bit % 8) != 7) bit++;
string str;
@ -948,14 +949,18 @@ string V3Number::toString() const {
}
V3Hash V3Number::toHash() const {
V3Hash hash(m_width);
for (int i = 0; i < words(); ++i) { hash += m_value[i].m_value; }
V3Hash hash{width()};
if (isString()) {
hash += V3Hash{m_data.str()};
} else {
for (int i = 0; i < words(); ++i) { hash += m_data.num()[i].m_value; }
}
return hash;
}
uint32_t V3Number::edataWord(int eword) const {
UASSERT(!isFourState(), "edataWord with 4-state " << *this);
return m_value[eword].m_value;
return m_data.num()[eword].m_value;
}
uint8_t V3Number::dataByte(int byte) const {
@ -963,30 +968,34 @@ uint8_t V3Number::dataByte(int byte) const {
}
bool V3Number::isAllZ() const {
if (isDouble() || isString()) return false;
for (int i = 0; i < width(); i++) {
if (!bitIsZ(i)) return false;
}
return true;
}
bool V3Number::isAllX() const {
if (isDouble() || isString()) return false;
uint32_t mask = hiWordMask();
for (int i = words() - 1; i >= 0; --i) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
if ((v.m_value & v.m_valueX) ^ mask) return false;
mask = ~0U;
}
return true;
}
bool V3Number::isEqZero() const {
if (isString()) return m_data.str().empty();
for (int i = 0; i < words(); i++) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
if (v.m_value || v.m_valueX) return false;
}
return true;
}
bool V3Number::isNeqZero() const {
if (isString()) return !m_data.str().empty();
for (int i = 0; i < words(); i++) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
if (v.m_value & ~v.m_valueX) return true;
}
return false;
@ -998,9 +1007,9 @@ bool V3Number::isBitsZero(int msb, int lsb) const {
return true;
}
bool V3Number::isEqOne() const {
if (m_value[0].m_value != 1 || m_value[0].m_valueX) return false;
if (m_data.num()[0].m_value != 1 || m_data.num()[0].m_valueX) return false;
for (int i = 1; i < words(); i++) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
if (v.m_value || v.m_valueX) return false;
}
return true;
@ -1015,7 +1024,7 @@ bool V3Number::isEqAllOnes(int optwidth) const {
bool V3Number::isFourState() const {
if (isDouble() || isString()) return false;
for (int i = 0; i < words(); ++i) {
if (m_value[i].m_valueX) return true;
if (m_data.num()[i].m_valueX) return true;
}
return false;
}
@ -1036,10 +1045,10 @@ bool V3Number::isAnyZ() const {
}
bool V3Number::isLtXZ(const V3Number& rhs) const {
// Include X/Z in comparisons for sort ordering
for (int bit = 0; bit < std::max(this->width(), rhs.width()); bit++) {
if (this->bitIs1(bit) && rhs.bitIs0(bit)) return true;
if (rhs.bitIs1(bit) && this->bitIs0(bit)) return false;
if (this->bitIsXZ(bit)) return true;
for (int bit = 0; bit < std::max(width(), rhs.width()); bit++) {
if (bitIs1(bit) && rhs.bitIs0(bit)) return true;
if (rhs.bitIs1(bit) && bitIs0(bit)) return false;
if (bitIsXZ(bit)) return true;
if (rhs.bitIsXZ(bit)) return false;
}
return false;
@ -1070,7 +1079,7 @@ int V3Number::widthMin() const {
uint32_t V3Number::countBits(const V3Number& ctrl) const {
int n = 0;
for (int bit = 0; bit < this->width(); ++bit) {
for (int bit = 0; bit < width(); ++bit) {
switch (ctrl.bitIs(0)) {
case '0':
if (bitIs0(bit)) ++n;
@ -1101,14 +1110,14 @@ uint32_t V3Number::countBits(const V3Number& ctrl1, const V3Number& ctrl2,
uint32_t V3Number::countOnes() const {
int n = 0;
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (bitIs1(bit)) n++;
}
return n;
}
uint32_t V3Number::mostSetBitP1() const {
for (int bit = this->width() - 1; bit >= 0; bit--) {
for (int bit = width() - 1; bit >= 0; bit--) {
if (bitIs1(bit)) return bit + 1;
}
return 0;
@ -1120,7 +1129,7 @@ V3Number& V3Number::opBitsNonX(const V3Number& lhs) { // 0/1->1, X/Z->0
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs0(bit) || lhs.bitIs1(bit)) setBit(bit, 1);
}
return *this;
@ -1130,7 +1139,7 @@ V3Number& V3Number::opBitsOne(const V3Number& lhs) { // 1->1, 0/X/Z->0
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs1(bit)) setBit(bit, 1);
}
return *this;
@ -1140,7 +1149,7 @@ V3Number& V3Number::opBitsXZ(const V3Number& lhs) { // 0/1->1, X/Z->0
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIsXZ(bit)) setBit(bit, 1);
}
return *this;
@ -1150,7 +1159,7 @@ V3Number& V3Number::opBitsZ(const V3Number& lhs) { // 0/1->1, X/Z->0
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIsZ(bit)) setBit(bit, 1);
}
return *this;
@ -1160,7 +1169,7 @@ V3Number& V3Number::opBitsNonZ(const V3Number& lhs) { // 0/1->1, X/Z->0
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (!lhs.bitIsZ(bit)) setBit(bit, 1);
}
return *this;
@ -1226,7 +1235,7 @@ V3Number& V3Number::opCountBits(const V3Number& expr, const V3Number& ctrl1, con
NUM_ASSERT_OP_ARGS4(expr, ctrl1, ctrl2, ctrl3);
NUM_ASSERT_LOGIC_ARGS4(expr, ctrl1, ctrl2, ctrl3);
setZero();
m_value[0].m_value = expr.countBits(ctrl1, ctrl2, ctrl3);
m_data.num()[0].m_value = expr.countBits(ctrl1, ctrl2, ctrl3);
opCleanThis();
return *this;
}
@ -1235,7 +1244,7 @@ V3Number& V3Number::opCountOnes(const V3Number& lhs) {
NUM_ASSERT_LOGIC_ARGS1(lhs);
if (lhs.isFourState()) return setAllBitsX();
setZero();
m_value[0].m_value = lhs.countOnes();
m_data.num()[0].m_value = lhs.countOnes();
opCleanThis();
return *this;
}
@ -1292,7 +1301,7 @@ V3Number& V3Number::opNot(const V3Number& lhs) {
NUM_ASSERT_LOGIC_ARGS1(lhs);
// op i, L(lhs) bit return
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs0(bit)) {
setBit(bit, 1);
} else if (lhs.bitIsXZ(bit)) {
@ -1307,7 +1316,7 @@ V3Number& V3Number::opAnd(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
// i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend.
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs1(bit) && rhs.bitIs1(bit)) {
setBit(bit, 1);
} else if (lhs.bitIs0(bit) || rhs.bitIs0(bit)) { // 0
@ -1323,7 +1332,7 @@ V3Number& V3Number::opOr(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
// i op j, max(L(lhs),L(rhs)) bit return, careful need to X/Z extend.
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs1(bit) || rhs.bitIs1(bit)) {
setBit(bit, 1);
} else if (lhs.bitIs0(bit) && rhs.bitIs0(bit)) {
@ -1340,7 +1349,7 @@ V3Number& V3Number::opXor(const V3Number& lhs, const V3Number& rhs) {
NUM_ASSERT_OP_ARGS2(lhs, rhs);
NUM_ASSERT_LOGIC_ARGS2(lhs, rhs);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (lhs.bitIs1(bit) && rhs.bitIs0(bit)) {
setBit(bit, 1);
} else if (lhs.bitIs0(bit) && rhs.bitIs1(bit)) {
@ -1619,9 +1628,9 @@ bool V3Number::isCaseEq(const V3Number& rhs) const {
// i op j, 1 bit return, max(L(lhs),L(rhs)) calculation, careful need to X/Z extend.
if (isString()) return toString() == rhs.toString();
if (isDouble()) return toDouble() == rhs.toDouble();
if (this->width() != rhs.width()) return false;
if (width() != rhs.width()) return false;
for (int i = 0; i < words(); ++i) {
if (!(m_value[i] == rhs.m_value[i])) return false;
if (!(m_data.num()[i] == rhs.m_data.num()[i])) return false;
}
return true;
}
@ -1754,7 +1763,7 @@ V3Number& V3Number::opShiftR(const V3Number& lhs, const V3Number& rhs) {
}
const uint32_t rhsval = rhs.toUInt();
if (rhsval < static_cast<uint32_t>(lhs.width())) {
for (int bit = 0; bit < this->width(); bit++) setBit(bit, lhs.bitIs(bit + rhsval));
for (int bit = 0; bit < width(); bit++) setBit(bit, lhs.bitIs(bit + rhsval));
}
return *this;
}
@ -1768,7 +1777,7 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t
if (rhs.isFourState()) return setAllBitsX();
setZero();
for (int bit = 32; bit < rhs.width(); bit++) {
for (int sbit = 0; sbit < this->width(); sbit++) {
for (int sbit = 0; sbit < width(); sbit++) {
setBit(sbit, lhs.bitIs(lbits - 1)); // 0/1/X/Z
}
if (rhs.bitIs1(lbits - 1)) setAllBits1(); // -1 else 0
@ -1776,11 +1785,11 @@ V3Number& V3Number::opShiftRS(const V3Number& lhs, const V3Number& rhs, uint32_t
}
const uint32_t rhsval = rhs.toUInt();
if (rhsval < static_cast<uint32_t>(lhs.width())) {
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
setBit(bit, lhs.bitIsExtend(bit + rhsval, lbits));
}
} else {
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
setBit(bit, lhs.bitIs(lbits - 1)); // 0/1/X/Z
}
}
@ -1797,7 +1806,7 @@ V3Number& V3Number::opShiftL(const V3Number& lhs, const V3Number& rhs) {
if (rhs.bitIs1(bit)) return *this; // shift of over 2^32 must be zero
}
const uint32_t rhsval = rhs.toUInt();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (bit >= static_cast<int>(rhsval)) setBit(bit, lhs.bitIs(bit - rhsval));
}
return *this;
@ -1825,7 +1834,7 @@ V3Number& V3Number::opAdd(const V3Number& lhs, const V3Number& rhs) {
setZero();
// Addem
int carry = 0;
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
const int sum = ((lhs.bitIs1(bit) ? 1 : 0) + (rhs.bitIs1(bit) ? 1 : 0) + carry);
if (sum & 1) setBit(bit, 1);
carry = (sum >= 2);
@ -1852,15 +1861,15 @@ V3Number& V3Number::opMul(const V3Number& lhs, const V3Number& rhs) {
opCleanThis(); // Mult produces extra bits in result
} else {
for (int lword = 0; lword < lhs.words(); lword++) {
const uint64_t lwordval = static_cast<uint64_t>(lhs.m_value[lword].m_value);
const uint64_t lwordval = static_cast<uint64_t>(lhs.m_data.num()[lword].m_value);
if (lwordval == 0) continue;
for (int rword = 0; rword < rhs.words(); rword++) {
const uint64_t rwordval = static_cast<uint64_t>(rhs.m_value[rword].m_value);
const uint64_t rwordval = static_cast<uint64_t>(rhs.m_data.num()[rword].m_value);
if (rwordval == 0) continue;
uint64_t mul = lwordval * rwordval;
for (int qword = lword + rword; qword < this->words(); qword++) {
mul += static_cast<uint64_t>(m_value[qword].m_value);
m_value[qword].m_value = (mul & 0xffffffffULL);
for (int qword = lword + rword; qword < words(); qword++) {
mul += static_cast<uint64_t>(m_data.num()[qword].m_value);
m_data.num()[qword].m_value = (mul & 0xffffffffULL);
mul = (mul >> 32ULL) & 0xffffffffULL;
if (mul == 0) break;
}
@ -1977,17 +1986,18 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
if (vw == 1) { // Single divisor word breaks rest of algorithm
uint64_t k = 0;
for (int j = uw - 1; j >= 0; j--) {
const uint64_t unw64 = ((k << 32ULL) + static_cast<uint64_t>(lhs.m_value[j].m_value));
m_value[j].m_value = unw64 / static_cast<uint64_t>(rhs.m_value[0].m_value);
const uint64_t unw64
= ((k << 32ULL) + static_cast<uint64_t>(lhs.m_data.num()[j].m_value));
m_data.num()[j].m_value = unw64 / static_cast<uint64_t>(rhs.m_data.num()[0].m_value);
k = unw64
- (static_cast<uint64_t>(m_value[j].m_value)
* static_cast<uint64_t>(rhs.m_value[0].m_value));
- (static_cast<uint64_t>(m_data.num()[j].m_value)
* static_cast<uint64_t>(rhs.m_data.num()[0].m_value));
}
UINFO(9, " opmoddiv-1w " << lhs << " " << rhs << " q=" << *this << " rem=0x" << std::hex
<< k << std::dec << endl);
if (is_modulus) {
setZero();
m_value[0].m_value = k;
m_data.num()[0].m_value = k;
}
opCleanThis();
return *this;
@ -1998,7 +2008,7 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
uint32_t vn[VL_MULS_MAX_WORDS + 1]; // v normalized
// Zero for ease of debugging and to save having to zero for shifts
for (int i = 0; i < words; i++) { m_value[i].m_value = 0; }
for (int i = 0; i < words; i++) { m_data.num()[i].m_value = 0; }
for (int i = 0; i < words + 1; i++) { un[i] = vn[i] = 0; } // +1 as vn may get extra word
// Algorithm requires divisor MSB to be set
@ -2006,22 +2016,22 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
const int s = 31 - ((vmsbp1 - 1) & 31); // shift amount (0...31)
const uint32_t shift_mask = s ? 0xffffffff : 0; // otherwise >> 32 won't mask the value
for (int i = vw - 1; i > 0; i--) {
vn[i] = (rhs.m_value[i].m_value << s)
| (shift_mask & (rhs.m_value[i - 1].m_value >> (32 - s)));
vn[i] = (rhs.m_data.num()[i].m_value << s)
| (shift_mask & (rhs.m_data.num()[i - 1].m_value >> (32 - s)));
}
vn[0] = rhs.m_value[0].m_value << s;
vn[0] = rhs.m_data.num()[0].m_value << s;
// Copy and shift dividend by same amount; may set new upper word
if (s) {
un[uw] = lhs.m_value[uw - 1].m_value >> (32 - s);
un[uw] = lhs.m_data.num()[uw - 1].m_value >> (32 - s);
} else {
un[uw] = 0;
}
for (int i = uw - 1; i > 0; i--) {
un[i] = (lhs.m_value[i].m_value << s)
| (shift_mask & (lhs.m_value[i - 1].m_value >> (32 - s)));
un[i] = (lhs.m_data.num()[i].m_value << s)
| (shift_mask & (lhs.m_data.num()[i - 1].m_value >> (32 - s)));
}
un[0] = lhs.m_value[0].m_value << s;
un[0] = lhs.m_data.num()[0].m_value << s;
// printf(" un="); for (int i=5; i>=0; i--) printf(" %08x",un[i]); printf("\n");
// printf(" vn="); for (int i=5; i>=0; i--) printf(" %08x",vn[i]); printf("\n");
@ -2052,11 +2062,11 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
}
t = un[j + vw] - k;
un[j + vw] = t;
this->m_value[j].m_value = qhat; // Save quotient digit
m_data.num()[j].m_value = qhat; // Save quotient digit
if (t < 0) {
// Over subtracted; correct by adding back
this->m_value[j].m_value--;
m_data.num()[j].m_value--;
k = 0;
for (int i = 0; i < vw; i++) {
t = static_cast<uint64_t>(un[i + j]) + static_cast<uint64_t>(vn[i]) + k;
@ -2074,9 +2084,9 @@ V3Number& V3Number::opModDivGuts(const V3Number& lhs, const V3Number& rhs, bool
if (is_modulus) { // modulus
// Need to reverse normalization on copy to output
for (int i = 0; i < vw; i++) {
m_value[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s)));
m_data.num()[i].m_value = (un[i] >> s) | (shift_mask & (un[i + 1] << (32 - s)));
}
for (int i = vw; i < words; i++) m_value[i].m_value = 0;
for (int i = vw; i < words; i++) m_data.num()[i].m_value = 0;
opCleanThis();
UINFO(9, " opmoddiv-mod " << lhs << " " << rhs << " now=" << *this << endl);
return *this;
@ -2110,7 +2120,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign,
}
if (lhs.isEqZero()) return setZero();
setZero();
m_value[0].m_value = 1;
m_data.num()[0].m_value = 1;
V3Number power(&lhs, width());
power.opAssign(lhs);
for (int bit = 0; bit < rhs.width(); bit++) {
@ -2122,7 +2132,7 @@ V3Number& V3Number::opPow(const V3Number& lhs, const V3Number& rhs, bool lsign,
if (rhs.bitIs1(bit)) { // out *= power
V3Number lastOut(&lhs, width());
lastOut.opAssign(*this);
this->opMul(lastOut, power);
opMul(lastOut, power);
// UINFO(0, "pow "<<lhs<<" "<<rhs<<" b"<<bit<<" pow="<<power<<" now="<<*this<<endl);
}
}
@ -2142,7 +2152,7 @@ V3Number& V3Number::opBufIf1(const V3Number& ens, const V3Number& if1s) {
NUM_ASSERT_OP_ARGS2(ens, if1s);
NUM_ASSERT_LOGIC_ARGS2(ens, if1s);
setZero();
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (ens.bitIs1(bit)) {
setBit(bit, if1s.bitIs(bit));
} else {
@ -2159,9 +2169,16 @@ V3Number& V3Number::opAssignNonXZ(const V3Number& lhs, bool ignoreXZ) {
// to itself; V3Simulate does this when hits "foo=foo;"
// So no: NUM_ASSERT_OP_ARGS1(lhs);
if (this != &lhs) {
setZero();
if (isString()) {
m_stringVal = lhs.m_stringVal;
if (VL_UNLIKELY(!lhs.isString())) {
// Non-compatible types, erase value.
m_data.str() = "";
} else {
m_data.str() = lhs.m_data.str();
}
} else if (VL_UNLIKELY(lhs.isString())) {
// Non-compatible types, erase value.
setZero();
} else {
// Also handles double as is just bits
for (int bit = 0; bit < this->width(); bit++) {
@ -2198,14 +2215,14 @@ V3Number& V3Number::opClean(const V3Number& lhs, uint32_t bits) { return opSel(l
void V3Number::opCleanThis(bool warnOnTruncation) {
// Clean MSB of number
NUM_ASSERT_LOGIC_ARGS1(*this);
const ValueAndX v = m_value[words() - 1];
const ValueAndX v = m_data.num()[words() - 1];
const uint32_t newValueMsb = v.m_value & hiWordMask();
const uint32_t newValueXMsb = v.m_valueX & hiWordMask();
if (warnOnTruncation && (newValueMsb != v.m_value || newValueXMsb != v.m_valueX)) {
// Displaying in decimal avoids hiWordMask truncation
v3warn(WIDTH, "Value too large for " << width() << " bit number: " << displayed("%d"));
}
m_value[words() - 1] = {newValueMsb, newValueXMsb};
m_data.num()[words() - 1] = {newValueMsb, newValueXMsb};
}
V3Number& V3Number::opSel(const V3Number& lhs, const V3Number& msb, const V3Number& lsb) {
@ -2221,7 +2238,7 @@ V3Number& V3Number::opSel(const V3Number& lhs, uint32_t msbval, uint32_t lsbval)
NUM_ASSERT_LOGIC_ARGS1(lhs);
setZero();
int ibit = lsbval;
for (int bit = 0; bit < this->width(); bit++) {
for (int bit = 0; bit < width(); bit++) {
if (ibit >= 0 && ibit < lhs.width() && ibit <= static_cast<int>(msbval)) {
setBit(bit, lhs.bitIs(ibit));
} else {
@ -2317,21 +2334,17 @@ V3Number& V3Number::opRealToBits(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs);
NUM_ASSERT_DOUBLE_ARGS1(lhs);
// Conveniently our internal format is identical so we can copy bits...
if (lhs.width() != 64 || this->width() != 64) {
v3fatalSrc("Real operation on wrong sized number");
}
if (lhs.width() != 64 || width() != 64) v3fatalSrc("Real operation on wrong sized number");
m_data.setLogic();
opAssign(lhs);
m_double = false;
return *this;
}
V3Number& V3Number::opBitsToRealD(const V3Number& lhs) {
NUM_ASSERT_OP_ARGS1(lhs);
// Conveniently our internal format is identical so we can copy bits...
if (lhs.width() != 64 || this->width() != 64) {
v3fatalSrc("Real operation on wrong sized number");
}
if (lhs.width() != 64 || width() != 64) v3fatalSrc("Real operation on wrong sized number");
m_data.setDouble();
opAssign(lhs);
m_double = true;
return *this;
}
V3Number& V3Number::opNegateD(const V3Number& lhs) {

View File

@ -22,8 +22,10 @@
#include "V3Error.h"
#include "V3Hash.h"
#include "V3StdFuture.h"
#include <algorithm>
#include <array>
#include <cmath>
#include <limits>
#include <vector>
@ -42,8 +44,6 @@ class AstNode;
class AstNodeDType;
class FileLine;
// Holds a few entries of ValueAndX to avoid dynamic allocation in std::vector for less width of
// constants
class V3NumberData final {
public:
// TYPES
@ -57,53 +57,294 @@ public:
}
};
enum class V3NumberDataType : uint8_t {
UNINITIALIZED = 0,
LOGIC = 1,
DOUBLE = 2,
STRING = 3,
};
friend std::ostream& operator<<(std::ostream& os, const V3NumberDataType& rhs) {
switch (rhs) {
case V3NumberDataType::UNINITIALIZED: return os << "UNINITIALIZED";
case V3NumberDataType::LOGIC: return os << "LOGIC";
case V3NumberDataType::DOUBLE: return os << "DOUBLE";
case V3NumberDataType::STRING: return os << "STRING";
}
return os;
}
private:
// CONSTANTS
// At least 2 words (64 fourstate bits). 4 words (128 fourstate bits) in most cases,
// i.e. when std::string has 32 bytes.
static constexpr int INLINE_WORDS = vlstd::max(
static_cast<size_t>(2), vlstd::max(sizeof(std::string) / sizeof(ValueAndX),
sizeof(std::vector<ValueAndX>) / sizeof(ValueAndX)));
// When m_width > MAX_INLINE_WIDTH number is stored in m_dynamicNumber.
// Otherwise number is stored in m_inlineNumber.
static constexpr int MAX_INLINE_WIDTH = INLINE_WORDS * sizeof(ValueAndX) / 2 * 8;
// MEMBERS
static constexpr size_t m_inlinedSize = 2; // Can hold 64 bit without dynamic allocation
ValueAndX m_inlined[m_inlinedSize]; // Holds the beginning m_inlinedSize words
std::vector<ValueAndX> m_data; // Holds m_inlinedSize-th word and later
union {
std::array<ValueAndX, INLINE_WORDS> m_inlineNumber;
std::vector<ValueAndX> m_dynamicNumber;
std::string m_string;
};
int m_width = 0; // Width (in bits) as specified/calculated
V3NumberDataType m_type;
public:
bool m_sized : 1; // True if the user specified the width, else we track it.
bool m_signed : 1; // True if signed value
bool m_isNull : 1; // True if "null" versus normal 0
bool m_fromString : 1; // True if from string literal
bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width
public:
// CONSTRUCTORS
V3NumberData() {
for (size_t i = 0; i < m_inlinedSize; ++i) m_inlined[i] = {0, 0};
V3NumberData()
: m_type{V3NumberDataType::UNINITIALIZED}
, m_sized{false}
, m_signed{false}
, m_isNull{false}
, m_fromString{false}
, m_autoExtend{false} {}
~V3NumberData() { destroyStoredValue(); }
V3NumberData(const V3NumberData& other)
: m_width{other.m_width}
, m_type{other.m_type}
, m_sized{other.m_sized}
, m_signed{other.m_signed}
, m_isNull{other.m_isNull}
, m_fromString{other.m_fromString}
, m_autoExtend{other.m_autoExtend} {
if (other.isInlineNumber()) {
initInlineNumber(other.m_inlineNumber);
} else if (other.isDynamicNumber()) {
initDynamicNumber(other.m_dynamicNumber);
} else if (other.isString()) {
initString(other.m_string);
}
}
V3NumberData& operator=(const V3NumberData& other) {
if (other.isInlineNumber()) {
destroyStoredValue();
initInlineNumber(other.m_inlineNumber);
} else if (other.isDynamicNumber()) {
reinitWithOrAssignDynamicNumber(other.m_dynamicNumber);
} else if (other.isString()) {
reinitWithOrAssignString(other.m_string);
} else {
destroyStoredValue();
}
m_width = other.m_width;
m_type = other.m_type;
m_sized = other.m_sized;
m_signed = other.m_signed;
m_isNull = other.m_isNull;
m_fromString = other.m_fromString;
m_autoExtend = other.m_autoExtend;
return *this;
}
V3NumberData(V3NumberData&& other)
: m_width{other.m_width}
, m_type{other.m_type}
, m_sized{other.m_sized}
, m_signed{other.m_signed}
, m_isNull{other.m_isNull}
, m_fromString{other.m_fromString}
, m_autoExtend{other.m_autoExtend} {
if (other.isInlineNumber()) {
initInlineNumber(other.m_inlineNumber);
} else if (other.isDynamicNumber()) {
initDynamicNumber(std::move(other.m_dynamicNumber));
} else if (other.isString()) {
initString(std::move(other.m_string));
}
other.m_type = V3NumberDataType::UNINITIALIZED;
}
V3NumberData& operator=(V3NumberData&& other) {
if (other.isInlineNumber()) {
destroyStoredValue();
initInlineNumber(other.m_inlineNumber);
} else if (other.isDynamicNumber()) {
reinitWithOrAssignDynamicNumber(std::move(other.m_dynamicNumber));
} else if (other.isString()) {
reinitWithOrAssignString(std::move(other.m_string));
} else {
destroyStoredValue();
}
m_width = other.m_width;
m_type = other.m_type;
m_sized = other.m_sized;
m_signed = other.m_signed;
m_isNull = other.m_isNull;
m_fromString = other.m_fromString;
m_autoExtend = other.m_autoExtend;
other.m_type = V3NumberDataType::UNINITIALIZED;
return *this;
}
// ACCESSORS
ValueAndX* num() {
UASSERT(isNumber(), "`num` member accessed when data type is " << m_type);
return isInlineNumber() ? m_inlineNumber.data() : m_dynamicNumber.data();
}
const ValueAndX* num() const {
UASSERT(isNumber(), "`num` member accessed when data type is " << m_type);
return isInlineNumber() ? m_inlineNumber.data() : m_dynamicNumber.data();
}
std::string& str() {
UASSERT(isString(), "`str` member accessed when data type is " << m_type);
return m_string;
}
const std::string& str() const {
UASSERT(isString(), "`str` member accessed when data type is " << m_type);
return m_string;
}
int width() const { return m_width; }
V3NumberDataType type() const { return m_type; }
// METHODS
void ensureSizeAtLeast(size_t s) {
if (VL_UNLIKELY(s > m_data.size() + m_inlinedSize)) m_data.resize(s - m_inlinedSize);
void resize(int bitsCount) {
if (m_width == bitsCount) return;
if (bitsToWords(m_width) == bitsToWords(bitsCount)) {
m_width = bitsCount;
return;
}
if (isDynamicNumber()) {
if (bitsCount > MAX_INLINE_WIDTH) {
m_dynamicNumber.resize(bitsToWords(bitsCount));
} else {
const auto dynamicBits = std::move(m_dynamicNumber);
destroyDynamicNumber();
initInlineNumber();
std::memcpy(m_inlineNumber.data(), dynamicBits.data(), sizeof(m_inlineNumber));
}
} else if (isInlineNumber()) {
if (bitsCount > MAX_INLINE_WIDTH) {
const auto bits = m_inlineNumber;
initDynamicNumber(bitsToWords(bitsCount));
std::memcpy(m_dynamicNumber.data(), bits.data(), sizeof(bits));
}
}
m_width = bitsCount;
}
ValueAndX& operator[](size_t idx) {
UDEBUGONLY(UASSERT(idx < m_data.size() + m_inlinedSize,
"idx:" << idx << " size:" << m_data.size()
<< " inlinedSize:" << m_inlinedSize););
return idx >= m_inlinedSize ? m_data[idx - m_inlinedSize] : m_inlined[idx];
void setString() {
// If there has been a string already it is kept intact.
if (isString()) return;
if (isDynamicNumber()) destroyDynamicNumber();
initString();
m_type = V3NumberDataType::STRING;
}
void setString(std::string&& s) {
reinitWithOrAssignString(std::move(s));
m_type = V3NumberDataType::STRING;
}
void setString(const std::string& s) {
reinitWithOrAssignString(s);
m_type = V3NumberDataType::STRING;
}
void setDouble() {
destroyStoredValue();
if (!isInlineNumber()) initInlineNumber();
m_type = V3NumberDataType::DOUBLE;
resize(64);
}
void setLogic() {
if (isString()) destroyString();
if (!isNumber()) {
if (m_width <= MAX_INLINE_WIDTH) {
initInlineNumber();
} else {
initDynamicNumber(bitsToWords(m_width));
}
}
m_type = V3NumberDataType::LOGIC;
resize(m_width);
}
private:
static constexpr int bitsToWords(int bitsCount) { return (bitsCount + 31) / 32; }
bool isNumber() const {
return m_type == V3NumberDataType::DOUBLE || m_type == V3NumberDataType::LOGIC;
}
bool isInlineNumber() const {
return (m_width <= MAX_INLINE_WIDTH)
&& (m_type == V3NumberDataType::DOUBLE || m_type == V3NumberDataType::LOGIC);
}
bool isDynamicNumber() const {
return (m_width > MAX_INLINE_WIDTH) && (m_type == V3NumberDataType::LOGIC);
}
bool isString() const { return m_type == V3NumberDataType::STRING; }
template <typename... Args>
void initInlineNumber(Args&&... args) {
new (&m_inlineNumber) std::array<ValueAndX, INLINE_WORDS>(std::forward<Args>(args)...);
}
template <typename... Args>
void initDynamicNumber(Args&&... args) {
new (&m_dynamicNumber) std::vector<ValueAndX>(std::forward<Args>(args)...);
}
template <typename... Args>
void initString(Args&&... args) {
new (&m_string) std::string(std::forward<Args>(args)...);
}
void destroyDynamicNumber() { m_dynamicNumber.~vector(); }
void destroyString() { m_string.~string(); }
void destroyStoredValue() {
if (isString())
destroyString();
else if (isDynamicNumber())
destroyDynamicNumber();
}
template <typename T>
void reinitWithOrAssignDynamicNumber(T&& s) {
if (isDynamicNumber()) {
m_dynamicNumber = std::forward<T>(s);
return;
}
if (isString()) destroyString();
initDynamicNumber(std::forward<T>(s));
}
template <typename T>
void reinitWithOrAssignString(T&& s) {
if (isString()) {
m_string = std::forward<T>(s);
return;
}
if (isDynamicNumber()) destroyDynamicNumber();
initString(std::forward<T>(s));
}
const ValueAndX& operator[](size_t idx) const { return const_cast<V3NumberData&>(*this)[idx]; }
};
class V3Number final {
// TYPES
using ValueAndX = V3NumberData::ValueAndX;
using V3NumberDataType = V3NumberData::V3NumberDataType;
// MEMBERS
V3NumberData m_data;
AstNode* m_nodep = nullptr; // Parent node
FileLine* m_fileline = nullptr;
// Large 4-state number handling
int m_width; // Width as specified/calculated.
bool m_sized : 1; // True if the user specified the width, else we track it.
bool m_signed : 1; // True if signed value
bool m_double : 1; // True if double real value
bool m_isNull : 1; // True if "null" versus normal 0
bool m_isString : 1; // True if string
bool m_fromString : 1; // True if from string literal
bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width
FileLine* m_fileline;
AstNode* m_nodep; // Parent node
V3NumberData m_value; // Value and X/Z information
string m_stringVal; // If isString, the value of the string
// METHODS
V3Number& setSingleBits(char value);
V3Number& setString(const string& str) {
m_isString = true;
m_stringVal = str;
m_data.setString(str);
return *this;
}
void opCleanThis(bool warnOnTruncation = false);
@ -116,10 +357,10 @@ public:
V3Number& setLong(uint32_t value);
V3Number& setLongS(int32_t value);
V3Number& setDouble(double value);
void setBit(int bit, char value) { // Note must be pre-zeroed!
if (bit >= m_width) return;
void setBit(int bit, char value) { // Note: must be initialized as number and pre-zeroed!
if (bit >= m_data.width()) return;
const uint32_t mask = (1UL << (bit & 31));
ValueAndX& v = m_value[bit / 32];
ValueAndX& v = m_data.num()[bit / 32];
if (value == '0' || value == 0) {
v.m_value &= ~mask;
v.m_valueX &= ~mask;
@ -137,65 +378,71 @@ public:
private:
char bitIs(int bit) const {
if (bit >= m_width || bit < 0) {
if (bit >= m_data.width() || bit < 0) {
// We never sign extend
return '0';
}
const ValueAndX v = m_value[bit / 32];
const ValueAndX v = m_data.num()[bit / 32];
return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0)
| ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]);
}
char bitIsExtend(int bit, int lbits) const {
// lbits usually = width, but for C optimizations width=32_bits, lbits = 32_or_less
if (bit < 0) return '0';
UASSERT(lbits <= m_width, "Extend of wrong size");
UASSERT(lbits <= m_data.width(), "Extend of wrong size");
if (bit >= lbits) {
bit = lbits ? lbits - 1 : 0;
const ValueAndX v = m_value[bit / 32];
const ValueAndX v = m_data.num()[bit / 32];
// We do sign extend
return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0)
| ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]);
}
const ValueAndX v = m_value[bit / 32];
const ValueAndX v = m_data.num()[bit / 32];
return ("01zx"[(((v.m_value & (1UL << (bit & 31))) ? 1 : 0)
| ((v.m_valueX & (1UL << (bit & 31))) ? 2 : 0))]);
}
public:
bool bitIs0(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return !bitIsXZ(m_width - 1);
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return !bitIsXZ(m_data.width() - 1);
const ValueAndX v = m_data.num()[bit / 32];
return ((v.m_value & (1UL << (bit & 31))) == 0 && !(v.m_valueX & (1UL << (bit & 31))));
}
bool bitIs1(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return false;
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return false;
const ValueAndX v = m_data.num()[bit / 32];
return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31))));
}
bool bitIs1Extend(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return bitIs1Extend(m_width - 1);
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return bitIs1Extend(m_data.width() - 1);
const ValueAndX v = m_data.num()[bit / 32];
return ((v.m_value & (1UL << (bit & 31))) && !(v.m_valueX & (1UL << (bit & 31))));
}
bool bitIsX(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return bitIsZ(m_width - 1);
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return bitIsZ(m_data.width() - 1);
const ValueAndX v = m_data.num()[bit / 32];
return ((v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31))));
}
bool bitIsXZ(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return bitIsXZ(m_width - 1);
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return bitIsXZ(m_data.width() - 1);
const ValueAndX v = m_data.num()[bit / 32];
return ((v.m_valueX & (1UL << (bit & 31))));
}
bool bitIsZ(int bit) const {
if (!isNumber()) return false;
if (bit < 0) return false;
if (bit >= m_width) return bitIsZ(m_width - 1);
const ValueAndX v = m_value[bit / 32];
if (bit >= m_data.width()) return bitIsZ(m_data.width() - 1);
const ValueAndX v = m_data.num()[bit / 32];
return ((~v.m_value & (1UL << (bit & 31))) && (v.m_valueX & (1UL << (bit & 31))));
}
@ -217,10 +464,12 @@ private:
public:
// CONSTRUCTORS
explicit V3Number(AstNode* nodep) { init(nodep, 1); }
V3Number(AstNode* nodep, int width) { init(nodep, width); } // 0=unsized
V3Number(AstNode* nodep, int width) { // 0=unsized
init(nodep, width, width > 0);
}
V3Number(AstNode* nodep, int width, uint32_t value, bool sized = true) {
init(nodep, width, sized);
m_value[0].m_value = value;
m_data.num()[0].m_value = value;
opCleanThis();
}
// Create from a verilog 32'hxxxx number.
@ -233,15 +482,16 @@ public:
V3Number(VerilogStringLiteral, AstNode* nodep, const string& str);
class String {};
V3Number(String, AstNode* nodep, const string& value) {
init(nodep, 0);
init(nodep);
setString(value);
m_fromString = true;
m_data.m_fromString = true;
}
class Null {};
V3Number(Null, AstNode* nodep) {
init(nodep, 0);
m_isNull = true;
m_autoExtend = true;
init(nodep);
m_data.setLogic();
m_data.m_isNull = true;
m_data.m_autoExtend = true;
}
explicit V3Number(const V3Number* nump, int width = 1) {
init(nullptr, width);
@ -249,7 +499,7 @@ public:
}
V3Number(const V3Number* nump, int width, uint32_t value) {
init(nullptr, width);
m_value[0].m_value = value;
m_data.num()[0].m_value = value;
opCleanThis();
m_fileline = nump->fileline();
}
@ -259,19 +509,31 @@ public:
}
V3Number(AstNode* nodep, const AstNodeDType* nodedtypep);
V3Number(const V3Number& other) = default;
V3Number& operator=(const V3Number& other) = default;
V3Number(V3Number&& other) = default;
V3Number& operator=(V3Number&& other) = default;
~V3Number() {}
private:
void V3NumberCreate(AstNode* nodep, const char* sourcep, FileLine* fl);
void init(AstNode* nodep, int swidth, bool sized = true) {
void init(AstNode* nodep, int swidth = -1, bool sized = true) {
setNames(nodep);
// dtype info does NOT from nodep's dtype; nodep only for error reporting
m_signed = false;
m_double = false;
m_isNull = false;
m_isString = false;
m_autoExtend = false;
m_fromString = false;
width(swidth, sized);
for (int i = 0; i < words(); ++i) m_value[i] = {0, 0};
if (swidth >= 0) {
if (swidth == 0) {
swidth = 1;
sized = false;
}
m_data.setLogic();
m_data.resize(swidth);
m_data.m_sized = sized;
for (int i = 0; i < words(); ++i) m_data.num()[i] = {0, 0};
} else {
m_data.resize(1);
m_data.m_sized = false;
}
}
void setNames(AstNode* nodep);
static string displayPad(size_t fmtsize, char pad, bool left, const string& in);
@ -282,15 +544,8 @@ public:
void v3errorEnd(std::ostringstream& sstr) const;
void v3errorEndFatal(std::ostringstream& sstr) const VL_ATTR_NORETURN;
void width(int width, bool sized = true) {
// Set width. Only set m_width here, as we need to tweak vector size
if (width) {
m_sized = sized;
m_width = width;
} else {
m_sized = false;
m_width = 1;
}
m_value.ensureSizeAtLeast(words());
m_data.m_sized = sized;
m_data.resize(width);
}
// SETTERS
@ -305,25 +560,39 @@ public:
string ascii(bool prefixed = true, bool cleanVerilog = false) const;
string displayed(AstNode* nodep, const string& vformat) const;
static bool displayedFmtLegal(char format, bool isScan); // Is this a valid format letter?
int width() const { return m_width; }
int width() const { return m_data.width(); }
int widthMin() const; // Minimum width that can represent this number (~== log2(num)+1)
bool sized() const { return m_sized; }
bool autoExtend() const { return m_autoExtend; }
bool isFromString() const { return m_fromString; }
bool sized() const { return m_data.m_sized; }
bool autoExtend() const { return m_data.m_autoExtend; }
bool isFromString() const { return m_data.m_fromString; }
V3NumberDataType dataType() const { return m_data.type(); }
void dataType(V3NumberDataType newType) {
if (dataType() == newType) return;
UASSERT(newType != V3NumberDataType::UNINITIALIZED, "Can't set type to UNINITIALIZED.");
switch (newType) {
case V3NumberDataType::STRING: m_data.setString(); break;
case V3NumberDataType::DOUBLE: m_data.setDouble(); break;
case V3NumberDataType::LOGIC: m_data.setLogic(); break;
case V3NumberDataType::UNINITIALIZED: break;
}
}
// Only correct for parsing of numbers from strings, otherwise not used
// (use AstConst::isSigned())
bool isSigned() const { return m_signed; }
// Only correct for parsing of numbers from strings, otherwise not used
// (use AstConst::isSigned())
bool isDouble() const { return m_double; }
// Only if have 64 bit value loaded, and want to indicate it's real
bool isString() const { return m_isString; }
bool isNegative() const { return bitIs1(width() - 1); }
bool isNull() const { return m_isNull; }
bool isSigned() const { return m_data.m_signed; }
void isSigned(bool ssigned) { m_data.m_signed = ssigned; }
bool isDouble() const { return dataType() == V3NumberDataType::DOUBLE; }
bool isString() const { return dataType() == V3NumberDataType::STRING; }
bool isNumber() const {
return m_data.type() == V3NumberDataType::LOGIC
|| m_data.type() == V3NumberDataType::DOUBLE;
}
bool isNegative() const { return !isString() && bitIs1(width() - 1); }
bool isNull() const { return m_data.m_isNull; }
bool isFourState() const;
bool hasZ() const {
if (isString()) return false;
for (int i = 0; i < words(); i++) {
const ValueAndX v = m_value[i];
const ValueAndX v = m_data.num()[i];
if ((~v.m_value) & v.m_valueX) return true;
}
return false;
@ -337,11 +606,10 @@ public:
bool isEqAllOnes(int optwidth = 0) const;
bool isCaseEq(const V3Number& rhs) const; // operator==
bool isLtXZ(const V3Number& rhs) const; // operator< with XZ compared
void isSigned(bool ssigned) { m_signed = ssigned; }
bool isAnyX() const;
bool isAnyXZ() const;
bool isAnyZ() const;
bool isMsbXZ() const { return bitIsXZ(m_width - 1); }
bool isMsbXZ() const { return bitIsXZ(m_data.width() - 1); }
uint32_t toUInt() const;
int32_t toSInt() const;
uint64_t toUQuad() const;

View File

@ -1013,8 +1013,8 @@ void V3Options::parseOptsList(FileLine* fl, const string& optdir, int argc, char
m_bboxUnsup = flag;
FileLine::globalWarnOff(V3ErrorCode::E_UNSUPPORTED, true);
});
DECL_OPTION("-bin", Set, &m_bin);
DECL_OPTION("-build", Set, &m_build);
DECL_OPTION("-build-dep-bin", Set, &m_buildDepBin);
DECL_OPTION("-build-jobs", CbVal, [this, fl](const char* valp) {
int val = std::atoi(valp);
if (val < 0) {

View File

@ -320,7 +320,7 @@ private:
int m_compLimitMembers = 64; // compiler selection; number of members in struct before make anon array
int m_compLimitParens = 240; // compiler selection; number of nested parens
string m_bin; // main switch: --bin {binary}
string m_buildDepBin; // main switch: --build-dep-bin {filename}
string m_exeName; // main switch: -o {name}
string m_flags; // main switch: -f {name}
string m_l2Name; // main switch: --l2name; "" for top-module's name
@ -419,7 +419,6 @@ public:
bool makePhony() const { return m_makePhony; }
bool preprocNoLine() const { return m_preprocNoLine; }
bool underlineZero() const { return m_underlineZero; }
string bin() const { return m_bin; }
string flags() const { return m_flags; }
bool systemC() const { return m_systemC; }
bool savable() const { return m_savable; }
@ -431,6 +430,8 @@ public:
bool bboxSys() const { return m_bboxSys; }
bool bboxUnsup() const { return m_bboxUnsup; }
bool build() const { return m_build; }
string buildDepBin() const { return m_buildDepBin; }
void buildDepBin(const string& flag) { m_buildDepBin = flag; }
bool cdc() const { return m_cdc; }
bool cmake() const { return m_cmake; }
bool context() const { return m_context; }
@ -631,7 +632,6 @@ public:
// Return options for child hierarchical blocks when forTop==false, otherwise returns args for
// the top module.
string allArgsStringForHierBlock(bool forTop) const;
void bin(const string& flag) { m_bin = flag; }
void parseOpts(FileLine* fl, int argc, char** argv);
void parseOptsList(FileLine* fl, const string& optdir, int argc, char** argv);
void parseOptsFile(FileLine* fl, const string& filename, bool rel);

30
src/V3StdFuture.h Normal file
View File

@ -0,0 +1,30 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//*************************************************************************
// DESCRIPTION: Verilator: Backports of features from future C++ versions
//
// Code available from: https://verilator.org
//
//*************************************************************************
//
// Copyright 2003-2022 by Wilson Snyder. This program is free software; you
// can redistribute it and/or modify it under the terms of either the GNU
// Lesser General Public License Version 3 or the Perl Artistic License
// Version 2.0.
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
//
//*************************************************************************
#ifndef VERILATOR_V3STDFUTURE_H_
#define VERILATOR_V3STDFUTURE_H_
namespace vlstd {
// constexpr std::max with arguments passed by value (required by constexpr before C++14)
template <class T>
constexpr T max(T a, T b) {
return a > b ? a : b;
}
}; // namespace vlstd
#endif // Guard

View File

@ -57,11 +57,16 @@
//
//
// Another thing done in this phase is signal strength handling.
// Currently they are only supported in assignments and only in case when the strongest assignment
// has constant with all bits equal on the RHS. It is the case when they can be statically
// resolved.
// Currently they are only supported in assignments and gates parsed as assignments (see verilog.y)
// and only in case when the strongest assignment has RHS marked as non-tristate. It is the case
// when they can be statically resolved.
// If the RHS is equal to z, that assignment has to be skipped. Since the value may be not known at
// verilation time, cases with tristates on RHS can't be handled statically.
//
// Static resolution is done in the following way:
// Static resolution is split into 2 parts.
// First part can be done before tristate propagation. It is about removing assignments that are
// weaker or equally strong as the strongest assignment with constant on RHS that has all bits
// the same (equal to 0 or 1). It is done in the following way:
// - The assignment of value 0 (size may be greater than 1), that has greatest strength (the
// one corresponding to 0) of all other assignments of 0, has to be found.
// - The same is done for value '1 and strength corresponding to value 1.
@ -72,16 +77,19 @@
// both strengths non-greater that the one was found, because they are weaker no matter what is on
// RHS.
//
// All assignments that are stronger than the one with strongest constant are left as they are.
// Second part of static resolution is done after tristate propagation.
// At that moment it is known that some expressions can't be equal to z. The exact value is
// unknown (except the ones with constants that were handled before), so weaker of both strengths
// has to be taken into account. All weaker assignments can be safely removed. It is done in
// similar way to the first part:
// - The assignment with non-tristate RHS with the greatest weaker strength has to be found.
// - Then all not stronger assignments can be removed.
//
// All assignments that are stronger than the strongest with non-tristate RHS are left as they are.
//
// There is a possible problem with equally strong assignments, because multiple assignments with
// the same strength, but different values should result in x value, but these values are
// unsupported.
//
// Singal strength can also be used in simple logic gates parsed as assignments (see verilog.y),
// but these gates are then either removed (if they are weaker than the strongest constant) or
// handled as the gates witout signal strengths are handled now. In other words, gate with greater
// strength won't properly overwrite weaker driver.
//*************************************************************************
#include "config_build.h"
@ -269,6 +277,18 @@ public:
void associate(AstNode* fromp, AstNode* top) {
new V3GraphEdge(&m_graph, makeVertex(fromp), makeVertex(top), 1);
}
void deleteVerticesFromSubtreeRecurse(AstNode* nodep) {
if (!nodep) return;
// Skip vars, because they may be connected to more than one varref
if (!VN_IS(nodep, Var)) {
TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
if (vertexp) vertexp->unlinkDelete(&m_graph);
}
deleteVerticesFromSubtreeRecurse(nodep->op1p());
deleteVerticesFromSubtreeRecurse(nodep->op2p());
deleteVerticesFromSubtreeRecurse(nodep->op3p());
deleteVerticesFromSubtreeRecurse(nodep->op4p());
}
void setTristate(AstNode* nodep) { makeVertex(nodep)->isTristate(true); }
bool isTristate(AstNode* nodep) {
const TristateVertex* const vertexp = reinterpret_cast<TristateVertex*>(nodep->user5p());
@ -715,64 +735,93 @@ class TristateVisitor final : public TristateBaseVisitor {
return assignmentOfValueOnAllBits(*maxIt, value) ? *maxIt : nullptr;
}
void removeWeakerAssignments(Assigns& assigns) {
bool isAssignmentNotStrongerThanStrength(AstAssignW* assignp, uint8_t strength) {
// If the value of the RHS is known and has all bits equal, only strength corresponding to
// its value is taken into account. In opposite case, both strengths are compared.
const uint8_t strength0 = getStrength(assignp, 0);
const uint8_t strength1 = getStrength(assignp, 1);
return (strength0 <= strength && strength1 <= strength)
|| (strength0 <= strength && assignmentOfValueOnAllBits(assignp, 0))
|| (strength1 <= strength && assignmentOfValueOnAllBits(assignp, 1));
}
void removeNotStrongerAssignments(Assigns& assigns, AstAssignW* strongestp,
uint8_t greatestKnownStrength) {
// Weaker assignments are these assignments that can't change the final value of the net.
// If the value of the RHS is known, only strength corresponding to its value is taken into
// account. Assignments of constants that have bits both 0 and 1 are skipped here, because
// it would involve handling parts of bits separately.
// First, the strongest assignment, that has value on the RHS consisting of only 1 or only
// 0, has to be found.
AstAssignW* const strongest0p = getStrongestAssignmentOfValue(assigns, 0);
AstAssignW* const strongest1p = getStrongestAssignmentOfValue(assigns, 1);
AstAssignW* strongestp = nullptr;
uint8_t greatestKnownStrength = 0;
const auto getIfStrongest = [&](AstAssignW* const strongestCandidatep, bool value) {
if (!strongestCandidatep) return;
uint8_t strength = getStrength(strongestCandidatep, value);
if (strength >= greatestKnownStrength) {
greatestKnownStrength = strength;
strongestp = strongestCandidatep;
// They can be safely removed. Assignments of the same strength are also removed, because
// duplicates aren't needed. One problem is with 2 assignments of different values and
// equal strengths. It should result in assignment of x value, but these values aren't
// supported now.
auto removedIt = std::remove_if(assigns.begin(), assigns.end(), [&](AstAssignW* assignp) {
if (assignp == strongestp) return false;
if (isAssignmentNotStrongerThanStrength(assignp, greatestKnownStrength)) {
// Vertices corresponding to nodes from removed assignment's subtree have to be
// removed.
m_tgraph.deleteVerticesFromSubtreeRecurse(assignp);
VL_DO_DANGLING(pushDeletep(assignp->unlinkFrBack()), assignp);
return true;
}
};
getIfStrongest(strongest0p, 0);
getIfStrongest(strongest1p, 1);
return false;
});
assigns.erase(removedIt, assigns.end());
}
if (strongestp) {
// Then all weaker assignments can be safely removed.
// Assignments of the same strength are also removed, because duplicates aren't needed.
// One problem is with 2 assignments of different values and equal strengths. It should
// result in assignment of x value, but these values aren't supported now.
auto removedIt
= std::remove_if(assigns.begin(), assigns.end(), [&](AstAssignW* assignp) {
if (assignp == strongestp) return false;
const uint8_t strength0 = getStrength(assignp, 0);
const uint8_t strength1 = getStrength(assignp, 1);
const bool toRemove = (strength0 <= greatestKnownStrength
&& strength1 <= greatestKnownStrength)
|| (strength0 <= greatestKnownStrength
&& assignmentOfValueOnAllBits(assignp, 0))
|| (strength1 <= greatestKnownStrength
&& assignmentOfValueOnAllBits(assignp, 1));
if (toRemove) {
// Don't propagate tristate if its assignment is removed.
TristateVertex* const vertexp
= reinterpret_cast<TristateVertex*>(assignp->rhsp()->user5p());
if (vertexp) vertexp->isTristate(false);
VL_DO_DANGLING(pushDeletep(assignp->unlinkFrBack()), assignp);
return true;
}
return false;
});
assigns.erase(removedIt, assigns.end());
void removeAssignmentsNotStrongerThanUniformConstant() {
// If a stronger assignment of a constant with all bits equal to the same
// value (0 or 1), is found, all weaker assignments can be safely removed.
for (auto& varpAssigns : m_assigns) {
Assigns& assigns = varpAssigns.second;
if (assigns.size() > 1) {
AstAssignW* const strongest0p = getStrongestAssignmentOfValue(assigns, 0);
AstAssignW* const strongest1p = getStrongestAssignmentOfValue(assigns, 1);
AstAssignW* strongestp = nullptr;
uint8_t greatestKnownStrength = 0;
const auto getIfStrongest
= [&](AstAssignW* const strongestCandidatep, bool value) {
if (!strongestCandidatep) return;
uint8_t strength = getStrength(strongestCandidatep, value);
if (strength >= greatestKnownStrength) {
greatestKnownStrength = strength;
strongestp = strongestCandidatep;
}
};
getIfStrongest(strongest0p, 0);
getIfStrongest(strongest1p, 1);
if (strongestp) {
removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength);
}
}
}
}
void resolveMultipleNetAssignments() {
void removeAssignmentsNotStrongerThanNonTristate() {
// Similar function as removeAssignmentsNotStrongerThanUniformConstant, but here the
// assignments that have strength not stronger than the strongest assignment with
// non-tristate RHS are removed. Strengths are compared according to their smaller values,
// because the values of RHSs are unknown. (Assignments not stronger than strongest
// constant are already removed.)
for (auto& varpAssigns : m_assigns) {
if (varpAssigns.second.size() > 1) {
// first the static resolution is tried
removeWeakerAssignments(varpAssigns.second);
Assigns& assigns = varpAssigns.second;
if (assigns.size() > 1) {
auto maxIt = std::max_element(
assigns.begin(), assigns.end(), [&](AstAssignW* ap, AstAssignW* bp) {
if (m_tgraph.isTristate(ap)) return !m_tgraph.isTristate(bp);
if (m_tgraph.isTristate(bp)) return false;
const uint8_t minStrengthA
= std::min(getStrength(ap, 0), getStrength(ap, 1));
const uint8_t minStrengthB
= std::min(getStrength(bp, 0), getStrength(bp, 1));
return minStrengthA < minStrengthB;
});
// If RHSs of all assignments are tristate, 1st element is returned, so it is
// needed to check if it is non-tristate.
AstAssignW* const strongestp = m_tgraph.isTristate(*maxIt) ? nullptr : *maxIt;
if (strongestp) {
uint8_t greatestKnownStrength
= std::min(getStrength(strongestp, 0), getStrength(strongestp, 1));
removeNotStrongerAssignments(assigns, strongestp, greatestKnownStrength);
}
}
}
}
@ -1534,10 +1583,14 @@ class TristateVisitor final : public TristateBaseVisitor {
iterateChildren(nodep);
m_graphing = false;
}
// resolve multiple net assignments and signal strengths
resolveMultipleNetAssignments();
// Remove all assignments not stronger than the strongest uniform constant
removeAssignmentsNotStrongerThanUniformConstant();
// Use graph to find tristate signals
m_tgraph.graphWalk(nodep);
// Remove all assignments not stronger than the strongest non-tristate RHS
removeAssignmentsNotStrongerThanNonTristate();
// Build the LHS drivers map for this module
iterateChildren(nodep);
// Insert new logic for all tristates

View File

@ -565,7 +565,7 @@ static void verilate(const string& argString) {
UINFO(1, "Option --verilate: Start Verilation\n");
// Can we skip doing everything if times are ok?
V3File::addSrcDepend(v3Global.opt.bin());
V3File::addSrcDepend(v3Global.opt.buildDepBin());
if (v3Global.opt.skipIdentical().isTrue()
&& V3File::checkTimes(v3Global.opt.hierTopDataDir() + "/" + v3Global.opt.prefix()
+ "__verFiles.dat",
@ -716,7 +716,7 @@ int main(int argc, char** argv, char** /*env*/) {
V3PreShell::boot();
// Command option parsing
v3Global.opt.bin(argv[0]);
v3Global.opt.buildDepBin(argv[0]);
const string argString = V3Options::argString(argc - 1, argv + 1);
v3Global.opt.parseOpts(new FileLine{FileLine::commandLineFilename()}, argc - 1, argv + 1);

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 by Wilson Snyder. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(vlt => 1);
compile(
v_flags2 => ['--build-dep-bin', 'path_to_exe'],
);
file_grep("$Self->{obj_dir}/$Self->{VM_PREFIX}__ver.d", qr/path_to_exe/);
ok(1);
1;

View File

@ -0,0 +1,8 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2005 by Wilson Snyder.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/);
endmodule

View File

@ -4,15 +4,30 @@
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/);
module t (clk1, clk2);
input wire clk1;
input wire clk2;
wire a;
nor (pull0, weak1) n1(a, 0, 0);
assign (strong0, weak1) a = 0;
wire [1:0] b;
assign (weak0, supply1) b = '1;
assign b = clk1 ? '0 : 'z;
wire c = 1;
assign (weak0, pull1) c = clk1 & clk2;
always begin
if (!a) begin
if (!a && b === '1 && c) begin
$write("*-* All Finished *-*\n");
$finish;
end
else begin
$write("Error: a = %b, b = %b, c = %b ", a, b, c);
$write("expected: a = 0, b = 11, c = 1\n");
$stop;
end
end
endmodule

View File

@ -0,0 +1,21 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 by Antmicro Ltd. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
compile(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,35 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t (clk1, clk2);
input wire clk1;
input wire clk2;
wire (weak0, weak1) a = 0;
assign (strong0, supply1) a = clk1;
assign (pull0, pull1) a = 1;
wire b;
xor (strong0, strong1) (b, clk1, clk2);
and (weak0, pull1) (b, clk1, clk2);
wire [7:0] c;
assign (supply0, strong1) c = clk1 ? '1 : '0;
assign (weak0, supply1) c = '0;
assign (weak0, pull1) c = 'z;
always begin
if (a === clk1 && b === clk1 ^ clk2 && c[0] === clk1) begin
$write("*-* All Finished *-*\n");
$finish;
end
else begin
$write("Error: a = %b, b = %b, c[0] = %b, ", a, b, c[0]);
$write("expected: a = %b, b = %b, c[0] = %b\n", clk1, clk1 ^ clk2, clk1);
$stop;
end
end
endmodule

View File

@ -0,0 +1,25 @@
// -*- mode: C++; c-file-style: "cc-mode" -*-
//
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2010 by Yu-Sheng Lin.
// SPDX-License-Identifier: CC0-1.0
#include <verilated.h>
#include <verilated_vcd_c.h>
#include "Vt_trace_open_wrong_order.h"
using namespace std;
int main(int argc, char** argv) {
VerilatedContext ctx;
VerilatedVcdC tfp;
Vt_trace_open_wrong_order dut;
ctx.traceEverOn(true);
tfp.open("dump.vcd"); // Error! shall put to the next line!
dut.trace(&tfp, 99); // Error!
tfp.dump(0);
tfp.close();
return 0;
}

View File

@ -0,0 +1,30 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 by Yu-Sheng Lin. This program is free software; you
# can redistribute it and/or modify it under the terms of either the GNU
# Lesser General Public License Version 3 or the Perl Artistic License
# Version 2.0.
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
scenarios(simulator => 1);
if ($Self->{vlt_all}) {
compile(
verilator_flags2 => ["--cc --trace --exe $Self->{t_dir}/$Self->{name}.cpp"],
make_top_shell => 0,
make_main => 0,
);
} else {
compile(
);
}
execute(
fails => 1
);
file_grep($Self->{run_log_filename}, qr/::trace\(\)' shall not be called after 'VerilatedVcdC::open\(\)'/i);
ok(1);
1;

View File

@ -0,0 +1,8 @@
// DESCRIPTION: Verilator: Verilog dummy test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Yu-Sheng Lin.
// SPDX-License-Identifier: CC0-1.0
module t(input clk);
endmodule