Merge origin/master and resolve conflicts
This commit is contained in:
commit
3760eb9ecc
12
Changes
12
Changes
|
|
@ -49,6 +49,7 @@ Verilator 5.047 devel
|
|||
* Add VPI callback support to --main (#7145).
|
||||
* Add V3LiftExpr pass to lower impure expressions and calls (#7141) (#7164). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Add --func-recursion-depth CLI option (#7175) (#7179).
|
||||
* Add `+verilator+solver+file` (#7242).
|
||||
* Add MacOS support for address sanitizer memory limit (#7308). [Marco Bartoli]
|
||||
* Deprecate `--structs-packed` (#7222).
|
||||
* Improve assignment-compatibility type check (#2843) (#5666) (#7052). [Pawel Kojma, Antmicro Ltd.]
|
||||
|
|
@ -65,13 +66,16 @@ Verilator 5.047 devel
|
|||
* Optimize DFG peephole until a fixed point (#7309). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize comparisons with identical operands and $countones in DFG. [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize more patterns in DfgPeephole (#7332). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Optimize read references in DFG (#7354). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix recursive default assignment for sub-arrays (#4589) (#7202). [Julian Carrier]
|
||||
* Fix virtual interface member trigger convergence (#5116) (#7323). [Yilou Wang]
|
||||
* Fix shift width mismatch in constraint solver SMT emission (#5420) (#7265). [Yilou Wang]
|
||||
* Fix randomize size+element queue constraints (#5582) (#7225). [Rahul Behl, Testorrent USA, Inc.]
|
||||
* Fix null assignment to virtual interfaces (#5974) (#5990). [Maxim Fonarev]
|
||||
* Fix typedef scope resolution for parameterized class aliases (#5977) (#7319). [Nick Brereton]
|
||||
* Fix lambda coroutines (#6106) (#7135). [Nick Brereton]
|
||||
* Fix super constructor calls with local variables (#6214) (#6933). [Igor Zaworski, Antmicro Ltd.]
|
||||
* Fix parameter default comparison when value contains type cast (#6281) (#7369) (#6281). [Yilou Wang]
|
||||
* Fix `local::` false error in randomize() with on parameterized class (#6680) (#7293). [Yilou Wang]
|
||||
* Fix false recursive definition error (#6769) (#7118). [Alex Zhou]
|
||||
* Fix port assignment to large arrays (#6904).
|
||||
|
|
@ -85,9 +89,11 @@ Verilator 5.047 devel
|
|||
* Fix wide conditional short circuiting (#7155).
|
||||
* Fix eliminating assignments to DPI-read variables (#7158). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix std::randomize() in static function with static class members (#7167) (#7169). [Yilou Wang]
|
||||
* Fix resolving default/non-default type parameters (#7171) (#7346). [em2machine]
|
||||
* Fix recursive constant function in $unit scope (#7173) (#7174).
|
||||
* Fix class extend references between queues (#7195).
|
||||
* Fix library/hier_block tracing when top name is empty (#7200). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix virtual interface select from sub-interface instance (#7203) (#7370) (#7203). [Yilou Wang]
|
||||
* Fix VPI force of bit-selected signals (#7211) (#7301). [Christian Hecken]
|
||||
* Fix wrong $bits() for parameterized interface struct typedefs (#7218) (#7219). [em2machine]
|
||||
* Fix `dist` operator inside constraint if blocks (#7221) (#7224). [Rahul Behl, Testorrent USA, Inc.]
|
||||
|
|
@ -96,6 +102,7 @@ Verilator 5.047 devel
|
|||
* Fix internal error when derived class calls this.randomize() with inherited rand members (#7229) (#7234). [Yilou Wang]
|
||||
* Fix enum range constraints missing for rand variables in sub-objects (#7230) (#7235). [Yilou Wang]
|
||||
* Fix vpi_put_value release on non-continuous signal (#7231) (#7241). [Christian Hecken]
|
||||
* Fix functions in generate block resulting in 'Broken link in node' (#7236) (#7367). [em2machine]
|
||||
* Fix tracing of typedefed 1D packed arrays with --trace-structs (#7237). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix rand variable used as array index in constraint evaluated as constant (#7238) (#7247). [Yilou Wang]
|
||||
* Fix --hierarchical dropping arguments in -f/-F files (#7240). [Clara Sparks]
|
||||
|
|
@ -113,6 +120,11 @@ Verilator 5.047 devel
|
|||
* Fix lost `$stop` on implied assertion `$error` failures.
|
||||
* Fix wait() hang when interface uses process calls and VIF function (#7342). [Yilou Wang]
|
||||
* Fix error on illegal nand/nor binary operators (#7353).
|
||||
* Fix simple array assignment unrolling in slice optimization (#7359). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix missing temporary for DfgSplicePacked (#7361). [Geza Lore, Testorrent USA, Inc.]
|
||||
* Fix virtual interface function calls binding to wrong instance (#7363). [Yilou Wang]
|
||||
* Fix false ASSIGNIN on interface input port connections (#7365). [Yilou Wang]
|
||||
* Fix string `inside` queue (#7373).
|
||||
|
||||
|
||||
Verilator 5.046 2026-02-28
|
||||
|
|
|
|||
|
|
@ -585,6 +585,7 @@ description of these arguments.
|
|||
+verilator+quiet Minimize additional printing
|
||||
+verilator+rand+reset+<value> Set random reset technique
|
||||
+verilator+seed+<value> Set random seed
|
||||
+verilator+solver+file+<filename> Set random solver log filename
|
||||
+verilator+V Show verbose version and config
|
||||
+verilator+version Show version and exit
|
||||
+verilator+wno+unsatconstr+<value> Disable constraint warnings
|
||||
|
|
|
|||
|
|
@ -116,6 +116,12 @@ Options:
|
|||
simulation runtime random seed value. If zero or not specified picks a
|
||||
value from the system random number generator.
|
||||
|
||||
.. option:: +verilator+solver+file+<filename>
|
||||
|
||||
If specified, when the randomization solver is used, open the given
|
||||
filename for writing, and log all random solver commands and responses
|
||||
to it.
|
||||
|
||||
.. option:: +verilator+V
|
||||
|
||||
Shows the verbose version, including configuration information.
|
||||
|
|
|
|||
|
|
@ -3001,6 +3001,14 @@ std::string VerilatedContext::profVltFilename() const VL_MT_SAFE {
|
|||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_profVltFilename;
|
||||
}
|
||||
void VerilatedContext::solverLogFilename(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_solverLogFilename = flag;
|
||||
}
|
||||
std::string VerilatedContext::solverLogFilename() const VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
return m_ns.m_solverLogFilename;
|
||||
}
|
||||
void VerilatedContext::solverProgram(const std::string& flag) VL_MT_SAFE {
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_ns.m_solverProgram = flag;
|
||||
|
|
@ -3017,6 +3025,12 @@ void VerilatedContext::randReset(int val) VL_MT_SAFE {
|
|||
const VerilatedLockGuard lock{m_mutex};
|
||||
m_s.m_randReset = val;
|
||||
}
|
||||
|
||||
std::string VerilatedContext::timeWithUnitString() const VL_MT_SAFE {
|
||||
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
||||
* vl_time_multiplier(timeprecision() - timeunit());
|
||||
return vl_timescaled_double(simtimeInUnits);
|
||||
}
|
||||
void VerilatedContext::timeunit(int value) VL_MT_SAFE {
|
||||
if (value < 0) value = -value; // Stored as 0..15
|
||||
const VerilatedLockGuard lock{m_mutex};
|
||||
|
|
@ -3233,6 +3247,8 @@ void VerilatedContextImp::commandArgVl(const std::string& arg) {
|
|||
quiet(true);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+rand+reset+", u64, 0, 2)) {
|
||||
randReset(static_cast<int>(u64));
|
||||
} else if (commandArgVlString(arg, "+verilator+solver+file+", str)) {
|
||||
solverLogFilename(str);
|
||||
} else if (commandArgVlUint64(arg, "+verilator+wno+unsatconstr+", u64, 0, 1)) {
|
||||
warnUnsatConstr(u64 == 0); // wno means disable, so invert
|
||||
} else if (commandArgVlUint64(arg, "+verilator+seed+", u64, 1,
|
||||
|
|
@ -3326,7 +3342,7 @@ void VerilatedContext::statsPrintSummary() VL_MT_UNSAFE {
|
|||
const std::string endwhy = gotError() ? "$stop" : gotFinish() ? "$finish" : "end";
|
||||
const double simtimeInUnits = VL_TIME_Q() * vl_time_multiplier(timeunit())
|
||||
* vl_time_multiplier(timeprecision() - timeunit());
|
||||
const std::string simtime = vl_timescaled_double(simtimeInUnits);
|
||||
const std::string simtime = timeWithUnitString();
|
||||
const double walltime = statWallTimeSinceStart();
|
||||
const double cputime = statCpuTimeSinceStart();
|
||||
const std::string simtimePerf
|
||||
|
|
|
|||
|
|
@ -415,6 +415,7 @@ protected:
|
|||
std::string m_coverageFilename; // +coverage+file filename
|
||||
std::string m_profExecFilename; // +prof+exec+file filename
|
||||
std::string m_profVltFilename; // +prof+vlt filename
|
||||
std::string m_solverLogFilename; // SMT solver log filename
|
||||
std::string m_solverProgram; // SMT solver program
|
||||
bool m_warnUnsatConstr = true; // Warn on unsatisfied constraints
|
||||
VlOs::DeltaCpuTime m_cpuTimeStart{false}; // CPU time, starts when create first model
|
||||
|
|
@ -586,6 +587,8 @@ public:
|
|||
void time(uint64_t value) VL_MT_SAFE { m_s.m_time = value; }
|
||||
/// Advance current simulation time. See time() for side effect details
|
||||
void timeInc(uint64_t add) VL_MT_UNSAFE { m_s.m_time += add; }
|
||||
/// Return time as unit string
|
||||
std::string timeWithUnitString() const VL_MT_SAFE;
|
||||
/// Return time units as power-of-ten
|
||||
int timeunit() const VL_MT_SAFE { return -m_s.m_timeunit; }
|
||||
/// Set time units as power-of-ten
|
||||
|
|
@ -666,6 +669,9 @@ public:
|
|||
std::string profVltFilename() const VL_MT_SAFE;
|
||||
void profVltFilename(const std::string& flag) VL_MT_SAFE;
|
||||
|
||||
// Internal: Solver log filename
|
||||
std::string solverLogFilename() const VL_MT_SAFE;
|
||||
void solverLogFilename(const std::string& flag) VL_MT_SAFE;
|
||||
// Internal: SMT solver program
|
||||
std::string solverProgram() const VL_MT_SAFE;
|
||||
void solverProgram(const std::string& flag) VL_MT_SAFE;
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@
|
|||
|
||||
#include "verilated_random.h"
|
||||
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
|
|
@ -61,6 +62,9 @@ class VlRProcess final : private std::streambuf, public std::iostream {
|
|||
char m_readBuf[BUFFER_SIZE];
|
||||
char m_writeBuf[BUFFER_SIZE];
|
||||
|
||||
std::unique_ptr<std::ofstream> m_logfp; // Log file stream
|
||||
uint64_t m_logLastTime = ~0ULL; // Last timestamp for logfile
|
||||
|
||||
public:
|
||||
typedef std::streambuf::traits_type traits_type;
|
||||
|
||||
|
|
@ -69,8 +73,8 @@ protected:
|
|||
const char c2 = static_cast<char>(c);
|
||||
if (pbase() == pptr()) return 0;
|
||||
const size_t size = pptr() - pbase();
|
||||
log(" ", std::string(pbase(), size));
|
||||
const ssize_t n = ::write(m_writeFd, pbase(), size);
|
||||
// VL_PRINTF_MT("solver-write '%s'\n", std::string(pbase(), size).c_str());
|
||||
if (VL_UNLIKELY(n == -1)) perror("write");
|
||||
if (n <= 0) {
|
||||
wait_report();
|
||||
|
|
@ -91,6 +95,7 @@ protected:
|
|||
wait_report();
|
||||
return traits_type::eof();
|
||||
}
|
||||
log("< ", std::string(m_readBuf, n));
|
||||
setg(m_readBuf, m_readBuf, m_readBuf + n);
|
||||
return traits_type::to_int_type(m_readBuf[0]);
|
||||
}
|
||||
|
|
@ -104,6 +109,7 @@ public:
|
|||
: std::streambuf{}
|
||||
, std::iostream{this}
|
||||
, m_cmd{cmd} {
|
||||
logOpen();
|
||||
open(cmd);
|
||||
}
|
||||
|
||||
|
|
@ -175,6 +181,7 @@ public:
|
|||
return false;
|
||||
}
|
||||
|
||||
log("", "# Open: "s + cmd[0]);
|
||||
const pid_t pid = fork();
|
||||
if (VL_UNLIKELY(pid < 0)) {
|
||||
perror("VlRProcess::open: fork");
|
||||
|
|
@ -212,6 +219,35 @@ public:
|
|||
return false;
|
||||
#endif
|
||||
}
|
||||
|
||||
private:
|
||||
void logOpen() {
|
||||
const std::string filename = Verilated::threadContextp()->solverLogFilename();
|
||||
if (filename.empty()) return;
|
||||
m_logfp = std::make_unique<std::ofstream>(filename);
|
||||
if (m_logfp.get() && m_logfp.get()->fail()) m_logfp = nullptr;
|
||||
if (!m_logfp) {
|
||||
const std::string msg = "%Error: Can't write '"s + filename + "'";
|
||||
VL_FATAL_MT("", 0, "", msg.c_str());
|
||||
return;
|
||||
}
|
||||
*m_logfp << "# Verilator solver log\n";
|
||||
}
|
||||
void log(const std::string& prefix, const std::string& text) {
|
||||
if (VL_LIKELY(!m_logfp.get()) || text.empty()) return;
|
||||
if (m_logLastTime != Verilated::threadContextp()->time()) {
|
||||
m_logLastTime = Verilated::threadContextp()->time();
|
||||
*m_logfp << "# [" << Verilated::threadContextp()->timeWithUnitString() << "]\n";
|
||||
}
|
||||
std::size_t startPos = 0;
|
||||
while (1) {
|
||||
const std::size_t endPos = text.find('\n', startPos);
|
||||
if (endPos == std::string::npos) break;
|
||||
*m_logfp << prefix << text.substr(startPos, endPos - startPos) << '\n';
|
||||
startPos = endPos + 1;
|
||||
}
|
||||
if (startPos < text.length()) *m_logfp << prefix << text.substr(startPos) << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
static VlRProcess& getSolver() {
|
||||
|
|
|
|||
|
|
@ -132,6 +132,7 @@ set(HEADERS
|
|||
V3LinkLevel.h
|
||||
V3LinkParse.h
|
||||
V3LinkResolve.h
|
||||
V3LinkWith.h
|
||||
V3List.h
|
||||
V3Localize.h
|
||||
V3MemberMap.h
|
||||
|
|
@ -306,6 +307,7 @@ set(COMMON_SOURCES
|
|||
V3LinkLevel.cpp
|
||||
V3LinkParse.cpp
|
||||
V3LinkResolve.cpp
|
||||
V3LinkWith.cpp
|
||||
V3Localize.cpp
|
||||
V3MergeCond.cpp
|
||||
V3Name.cpp
|
||||
|
|
|
|||
|
|
@ -300,6 +300,7 @@ RAW_OBJS_PCH_ASTNOMT = \
|
|||
V3LinkLevel.o \
|
||||
V3LinkParse.o \
|
||||
V3LinkResolve.o \
|
||||
V3LinkWith.o \
|
||||
V3Localize.o \
|
||||
V3MergeCond.o \
|
||||
V3Name.o \
|
||||
|
|
|
|||
|
|
@ -474,6 +474,9 @@ private:
|
|||
AstEventControl* const controlp = new AstEventControl{
|
||||
nodep->fileline(), new AstSenTree{flp, sensesp->cloneTree(false)}, nullptr};
|
||||
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||
AstNodeExpr* throughoutp
|
||||
= nodep->throughoutp() ? nodep->throughoutp()->unlinkFrBack() : nullptr;
|
||||
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
|
|
@ -481,24 +484,71 @@ private:
|
|||
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, true};
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||
|
||||
// Throughout: create flag tracking whether condition held every tick
|
||||
AstVar* throughoutOkp = nullptr;
|
||||
if (throughoutp) {
|
||||
throughoutOkp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__throughoutOk",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::LOGIC_IMPLICIT)};
|
||||
throughoutOkp->lifetime(VLifetime::AUTOMATIC_EXPLICIT);
|
||||
beginp->addStmtsp(throughoutOkp);
|
||||
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
// Check condition at tick 0 (sequence start, before entering loop)
|
||||
AstSampled* const initSampledp
|
||||
= new AstSampled{flp, throughoutp->cloneTreePure(false)};
|
||||
initSampledp->dtypeSetBit();
|
||||
beginp->addStmtsp(
|
||||
new AstIf{flp, new AstLogNot{flp, initSampledp},
|
||||
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}});
|
||||
}
|
||||
|
||||
{
|
||||
AstLoop* const loopp = new AstLoop{flp};
|
||||
loopp->addStmtsp(new AstLoopTest{
|
||||
flp, loopp,
|
||||
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}}});
|
||||
// When throughout is present, exit loop early if condition fails
|
||||
AstNodeExpr* loopCondp
|
||||
= new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}};
|
||||
if (throughoutOkp) {
|
||||
loopCondp = new AstLogAnd{flp, loopCondp,
|
||||
new AstVarRef{flp, throughoutOkp, VAccess::READ}};
|
||||
}
|
||||
loopp->addStmtsp(new AstLoopTest{flp, loopp, loopCondp});
|
||||
loopp->addStmtsp(controlp);
|
||||
loopp->addStmtsp(
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}});
|
||||
// Check throughout condition at each tick during delay (IEEE 1800-2023 16.9.9)
|
||||
if (throughoutp) {
|
||||
AstSampled* const sampledp = new AstSampled{flp, throughoutp};
|
||||
sampledp->dtypeSetBit();
|
||||
loopp->addStmtsp(
|
||||
new AstIf{flp, new AstLogNot{flp, sampledp},
|
||||
new AstAssign{flp, new AstVarRef{flp, throughoutOkp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitFalse{}}}});
|
||||
}
|
||||
beginp->addStmtsp(loopp);
|
||||
}
|
||||
if (m_disableSeqIfp) {
|
||||
// Compose wrappers on remaining sequence: throughout gate (inner), disable iff (outer)
|
||||
AstNode* remainp = nodep->nextp() ? nodep->nextp()->unlinkFrBackWithNext() : nullptr;
|
||||
if (throughoutOkp) {
|
||||
// If condition failed during delay, fail assertion
|
||||
remainp = new AstIf{flp, new AstVarRef{flp, throughoutOkp, VAccess::READ}, remainp,
|
||||
new AstPExprClause{flp, /*pass=*/false}};
|
||||
}
|
||||
if (m_disableSeqIfp && remainp) {
|
||||
AstIf* const disableSeqIfp = m_disableSeqIfp->cloneTree(false);
|
||||
AstNode* const continuationsp = nodep->nextp()->unlinkFrBackWithNext();
|
||||
// Keep continuation statements in a proper statement-list container.
|
||||
disableSeqIfp->addThensp(new AstBegin{flp, "", continuationsp, true});
|
||||
nodep->addNextHere(disableSeqIfp);
|
||||
disableSeqIfp->addThensp(new AstBegin{flp, "", remainp, true});
|
||||
remainp = disableSeqIfp;
|
||||
}
|
||||
if (remainp) {
|
||||
if (throughoutOkp) {
|
||||
// throughoutOkp is declared in beginp scope -- check must be inside it
|
||||
beginp->addStmtsp(remainp);
|
||||
} else {
|
||||
nodep->addNextHere(remainp);
|
||||
}
|
||||
}
|
||||
nodep->replaceWith(beginp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
|
|
@ -843,7 +893,7 @@ private:
|
|||
return new AstPExpr{flp, beginp, exprp->findBitDType()};
|
||||
}
|
||||
|
||||
void visit(AstSExprGotoRep* nodep) override {
|
||||
void visit(AstSGotoRep* nodep) override {
|
||||
// Standalone goto rep (not inside implication antecedent)
|
||||
iterateChildren(nodep);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
|
@ -870,8 +920,8 @@ private:
|
|||
if (nodep->sentreep()) return; // Already processed
|
||||
|
||||
// Handle goto repetition as antecedent before iterateChildren,
|
||||
// so the standalone AstSExprGotoRep visitor doesn't process it
|
||||
if (AstSExprGotoRep* const gotop = VN_CAST(nodep->lhsp(), SExprGotoRep)) {
|
||||
// so the standalone AstSGotoRep visitor doesn't process it
|
||||
if (AstSGotoRep* const gotop = VN_CAST(nodep->lhsp(), SGotoRep)) {
|
||||
iterateChildren(gotop);
|
||||
iterateAndNextNull(nodep->rhsp());
|
||||
FileLine* const flp = nodep->fileline();
|
||||
|
|
|
|||
|
|
@ -650,6 +650,45 @@ class AssertPropLowerVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstSThroughout* nodep) override {
|
||||
// IEEE 1800-2023 16.9.9: expr throughout seq
|
||||
// Transform by AND-ing cond with every leaf expression in the sequence,
|
||||
// and attaching cond to every delay for per-tick checking in V3AssertPre.
|
||||
AstNodeExpr* const condp = nodep->lhsp()->unlinkFrBack();
|
||||
AstNodeExpr* const seqp = nodep->rhsp()->unlinkFrBack();
|
||||
if (AstSExpr* const sexprp = VN_CAST(seqp, SExpr)) {
|
||||
// Walk all SExpr nodes: AND cond with leaf expressions, attach to delays
|
||||
sexprp->foreach([&](AstSExpr* sp) {
|
||||
if (sp->exprp() && !VN_IS(sp->exprp(), SExpr)) {
|
||||
AstNodeExpr* const origp = sp->exprp()->unlinkFrBack();
|
||||
AstLogAnd* const andp
|
||||
= new AstLogAnd{origp->fileline(), condp->cloneTreePure(false), origp};
|
||||
andp->dtypeSetBit();
|
||||
sp->exprp(andp);
|
||||
}
|
||||
if (sp->preExprp() && !VN_IS(sp->preExprp(), SExpr)) {
|
||||
AstNodeExpr* const origp = sp->preExprp()->unlinkFrBack();
|
||||
AstLogAnd* const andp
|
||||
= new AstLogAnd{origp->fileline(), condp->cloneTreePure(false), origp};
|
||||
andp->dtypeSetBit();
|
||||
sp->preExprp(andp);
|
||||
}
|
||||
if (AstDelay* const dlyp = VN_CAST(sp->delayp(), Delay)) {
|
||||
dlyp->throughoutp(condp->cloneTreePure(false));
|
||||
}
|
||||
});
|
||||
nodep->replaceWith(sexprp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
VL_DO_DANGLING(condp->deleteTree(), condp);
|
||||
visit(sexprp);
|
||||
} else {
|
||||
// Single expression (no delay): degenerate to cond && seq
|
||||
AstLogAnd* const andp = new AstLogAnd{nodep->fileline(), condp, seqp};
|
||||
andp->dtypeSetBit();
|
||||
nodep->replaceWith(andp);
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
VL_RESTORER(m_modp);
|
||||
m_modp = nodep;
|
||||
|
|
@ -829,45 +868,69 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
struct SeqStep final {
|
||||
AstNodeExpr* exprp; // Expression to check (nullptr if unary leading delay)
|
||||
int delay; // Fixed delay after this expression (0 for tail)
|
||||
bool isRange; // Whether this step's delay is a range
|
||||
bool isRange; // Step's delay is a range
|
||||
bool isUnbounded; // Range is unbounded (rhs is AstUnbounded)
|
||||
int rangeMin;
|
||||
int rangeMax;
|
||||
int rangeMax; // -1 for unbounded
|
||||
};
|
||||
|
||||
// Extract delay bounds from AstDelay. Clones and constifies (does not modify original AST).
|
||||
bool extractDelayBounds(AstDelay* dlyp, bool& isRange, int& minVal, int& maxVal) {
|
||||
// For unbounded ranges (rhs is AstUnbounded), maxVal is set to -1; rhsp is not constified.
|
||||
bool extractDelayBounds(AstDelay* dlyp, bool& isRange, bool& isUnbounded, int& minVal,
|
||||
int& maxVal) {
|
||||
isRange = dlyp->isRangeDelay();
|
||||
isUnbounded = dlyp->isUnbounded();
|
||||
AstNodeExpr* const minExprp = V3Const::constifyEdit(dlyp->lhsp()->cloneTree(false));
|
||||
const AstConst* const minConstp = VN_CAST(minExprp, Const);
|
||||
if (isRange) {
|
||||
AstNodeExpr* const maxExprp = V3Const::constifyEdit(dlyp->rhsp()->cloneTree(false));
|
||||
const AstConst* const maxConstp = VN_CAST(maxExprp, Const);
|
||||
if (!minConstp || !maxConstp) {
|
||||
dlyp->v3error("Range delay bounds must be elaboration-time constants"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
if (isUnbounded) {
|
||||
// ##[M:$], ##[*], ##[+]: only min bound; max is open-ended
|
||||
if (!minConstp) {
|
||||
dlyp->v3error("Range delay minimum must be an elaboration-time constant"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
return false;
|
||||
}
|
||||
minVal = minConstp->toSInt();
|
||||
maxVal = -1;
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
if (minVal < 0) {
|
||||
dlyp->v3error("Range delay bounds must be non-negative"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
AstNodeExpr* const maxExprp
|
||||
= V3Const::constifyEdit(dlyp->rhsp()->cloneTree(false));
|
||||
const AstConst* const maxConstp = VN_CAST(maxExprp, Const);
|
||||
if (!minConstp || !maxConstp) {
|
||||
dlyp->v3error("Range delay bounds must be elaboration-time constants"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
VL_DO_DANGLING(maxExprp->deleteTree(), maxExprp);
|
||||
return false;
|
||||
}
|
||||
minVal = minConstp->toSInt();
|
||||
maxVal = maxConstp->toSInt();
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
VL_DO_DANGLING(maxExprp->deleteTree(), maxExprp);
|
||||
return false;
|
||||
}
|
||||
minVal = minConstp->toSInt();
|
||||
maxVal = maxConstp->toSInt();
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
VL_DO_DANGLING(maxExprp->deleteTree(), maxExprp);
|
||||
if (minVal < 0 || maxVal < 0) {
|
||||
dlyp->v3error("Range delay bounds must be non-negative"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
return false;
|
||||
}
|
||||
if (maxVal < minVal) {
|
||||
dlyp->v3error("Range delay maximum must be >= minimum"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
return false;
|
||||
}
|
||||
if (minVal == 0) {
|
||||
dlyp->v3warn(E_UNSUPPORTED, "Unsupported: ##0 in range delays");
|
||||
return false;
|
||||
if (minVal < 0 || maxVal < 0) {
|
||||
dlyp->v3error("Range delay bounds must be non-negative"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
return false;
|
||||
}
|
||||
if (maxVal < minVal) {
|
||||
dlyp->v3error("Range delay maximum must be >= minimum"
|
||||
" (IEEE 1800-2023 16.7)");
|
||||
return false;
|
||||
}
|
||||
if (minVal == 0) {
|
||||
dlyp->v3warn(E_UNSUPPORTED, "Unsupported: ##0 in bounded range delays");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
isUnbounded = false;
|
||||
minVal = maxVal = minConstp ? minConstp->toSInt() : 0;
|
||||
VL_DO_DANGLING(minExprp->deleteTree(), minExprp);
|
||||
}
|
||||
|
|
@ -891,127 +954,164 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
AstDelay* const dlyp = VN_CAST(curp->delayp(), Delay);
|
||||
UASSERT_OBJ(dlyp, curp, "Expected AstDelay");
|
||||
bool isRange = false;
|
||||
bool isUnbounded = false;
|
||||
int minVal = 0;
|
||||
int maxVal = 0;
|
||||
if (!extractDelayBounds(dlyp, isRange, minVal, maxVal)) return false;
|
||||
if (!extractDelayBounds(dlyp, isRange, isUnbounded, minVal, maxVal)) return false;
|
||||
if (isRange) hasRange = true;
|
||||
|
||||
if (curp->preExprp() && !VN_IS(curp->preExprp(), SExpr)) {
|
||||
steps.push_back({curp->preExprp(), minVal, isRange, minVal, maxVal});
|
||||
steps.push_back({curp->preExprp(), minVal, isRange, isUnbounded, minVal, maxVal});
|
||||
} else {
|
||||
steps.push_back({nullptr, minVal, isRange, minVal, maxVal});
|
||||
steps.push_back({nullptr, minVal, isRange, isUnbounded, minVal, maxVal});
|
||||
}
|
||||
|
||||
if (AstSExpr* const nextp = VN_CAST(curp->exprp(), SExpr)) {
|
||||
return linearizeImpl(nextp, steps, hasRange);
|
||||
}
|
||||
steps.push_back({curp->exprp(), 0, false, 0, 0});
|
||||
steps.push_back({curp->exprp(), 0, false, false, 0, 0});
|
||||
return true;
|
||||
}
|
||||
|
||||
// Build FSM body as if/else chain on state variable.
|
||||
// State 0 = IDLE. Each range delay adds 2 states (wait + check),
|
||||
// each fixed delay adds 1 (wait), each tail expr adds 1 (check).
|
||||
//
|
||||
// Example: a ##[M:N] b ##1 c
|
||||
// steps: [{a, range[M:N]}, {b, delay=1}, {c, delay=0}]
|
||||
// State 1: WAIT_MIN (count down M cycles)
|
||||
// State 2: CHECK_RANGE (check b each cycle, up to N-M retries)
|
||||
// State 3: WAIT_FIXED (count down 1 cycle for ##1)
|
||||
// State 4: CHECK_TAIL (check c, report pass/fail)
|
||||
AstNode* buildFsmBody(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp, AstVar* failVarp,
|
||||
const std::vector<SeqStep>& steps, AstSenItem* /*sensesp*/,
|
||||
AstNodeExpr* antExprp) {
|
||||
// Pre-assigned state numbers for one SeqStep.
|
||||
// Range steps consume their successor (check target); successor entry is unused.
|
||||
struct StepBounds final {
|
||||
int waitState; // WAIT_MIN state, or -1 if not needed
|
||||
int checkState; // CHECK or TAIL state; -1 for fixed-delay steps
|
||||
};
|
||||
|
||||
// Assign state numbers to all steps before building FSM bodies.
|
||||
//
|
||||
// State layout for a ##[M:N] b ##1 c (bounded, M>0):
|
||||
// State 0: IDLE -- detect trigger, launch FSM
|
||||
// State 1: WAIT_MIN -- count down M-1 cycles
|
||||
// State 2: CHECK -- sample b; fail after N-M retries
|
||||
// State 3: WAIT_FIX -- count down 1 cycle for ##1
|
||||
// State 4: TAIL -- sample c, report pass/fail
|
||||
//
|
||||
// For ##[M:$] b ... (unbounded, M>1): same as bounded but CHECK has no timeout.
|
||||
// For ##[+] b (unbounded, M=1): WAIT_MIN skipped; CHECK is state 1.
|
||||
// For ##[*] b (unbounded, M=0): handled in IDLE directly (no WAIT_MIN).
|
||||
//
|
||||
// LIMITATION: single-evaluation FSM -- overlapping triggers are ignored
|
||||
// while the FSM is active. For ##[M:$], if the consequent never becomes
|
||||
// true the FSM remains in CHECK indefinitely, blocking new evaluations.
|
||||
std::vector<StepBounds> preAssignStates(const std::vector<SeqStep>& steps) {
|
||||
std::vector<StepBounds> bounds(steps.size(), {-1, -1});
|
||||
int s = 1;
|
||||
for (size_t i = 0; i < steps.size(); ++i) {
|
||||
const SeqStep& step = steps[i];
|
||||
if (step.isRange) {
|
||||
// Unbounded with min<=1: no WAIT_MIN (counter starts at 0 in CHECK).
|
||||
const bool needsWait = !step.isUnbounded || step.rangeMin > 1;
|
||||
if (needsWait) bounds[i].waitState = s++;
|
||||
bounds[i].checkState = s++;
|
||||
++i; // step[i+1] is the check target, not a separate FSM state
|
||||
} else if (step.delay > 0) {
|
||||
bounds[i].waitState = s++;
|
||||
} else {
|
||||
bounds[i].checkState = s++; // tail check
|
||||
}
|
||||
}
|
||||
return bounds;
|
||||
}
|
||||
|
||||
// Build the match action for a range CHECK state.
|
||||
// isTail=true: return to IDLE; isTail=false: advance to afterMatchState.
|
||||
AstNode* makeOnMatchAction(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp, bool isTail,
|
||||
int afterMatchState, int nextDelay) {
|
||||
if (isTail) {
|
||||
return new AstAssign{flp, new AstVarRef{flp, stateVarp, VAccess::WRITE},
|
||||
new AstConst{flp, 0}};
|
||||
}
|
||||
return makeStateTransition(flp, stateVarp, cntVarp, afterMatchState,
|
||||
nextDelay > 0 ? nextDelay - 1 : 0);
|
||||
}
|
||||
|
||||
// Build the body of a range CHECK state.
|
||||
// Bounded: fail on timeout, decrement counter otherwise.
|
||||
// Unbounded: stay until match (no timeout).
|
||||
AstNode* makeRangeCheckBody(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp,
|
||||
AstVar* failVarp, AstNodeExpr* exprp, AstNode* matchActionp,
|
||||
bool isUnbounded) {
|
||||
if (isUnbounded) {
|
||||
return new AstIf{flp, new AstSampled{flp, exprp->cloneTree(false)}, matchActionp,
|
||||
nullptr};
|
||||
}
|
||||
AstBegin* const timeoutp = new AstBegin{flp, "", nullptr, true};
|
||||
timeoutp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, failVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
timeoutp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, stateVarp, VAccess::WRITE},
|
||||
new AstConst{flp, 0}});
|
||||
AstNode* const decrementp = new AstAssign{
|
||||
flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 1}}};
|
||||
AstIf* const failOrRetryp = new AstIf{
|
||||
flp, new AstEq{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}},
|
||||
timeoutp, decrementp};
|
||||
return new AstIf{flp, new AstSampled{flp, exprp->cloneTree(false)}, matchActionp,
|
||||
failOrRetryp};
|
||||
}
|
||||
|
||||
AstNode* buildFsmBody(FileLine* flp, AstVar* stateVarp, AstVar* cntVarp, AstVar* failVarp,
|
||||
const std::vector<SeqStep>& steps, AstNodeExpr* antExprp) {
|
||||
|
||||
const std::vector<StepBounds> bounds = preAssignStates(steps);
|
||||
AstNode* fsmChainp = nullptr;
|
||||
int nextState = 1;
|
||||
|
||||
for (size_t i = 0; i < steps.size(); ++i) {
|
||||
const SeqStep& step = steps[i];
|
||||
|
||||
if (step.isRange) {
|
||||
// Range delay needs two states: WAIT_MIN and CHECK_RANGE
|
||||
UASSERT(i + 1 < steps.size(), "Range must have next step");
|
||||
const int waitState = nextState++;
|
||||
const int checkState = nextState++;
|
||||
const int rangeWidth = step.rangeMax - step.rangeMin;
|
||||
const SeqStep& nextStep = steps[i + 1];
|
||||
const int afterMatchState = bounds[i].checkState + 1;
|
||||
const bool isTail = (i + 2 >= steps.size() && nextStep.delay == 0);
|
||||
|
||||
const int afterMatchState = nextState;
|
||||
|
||||
// WAIT_MIN: count down rangeMin cycles
|
||||
{
|
||||
AstNode* const bodyp = new AstIf{
|
||||
// WAIT_MIN state: count down rangeMin-1 cycles before entering CHECK
|
||||
if (bounds[i].waitState >= 0) {
|
||||
const int initCnt = step.isUnbounded ? 0 : (step.rangeMax - step.rangeMin);
|
||||
AstNode* const waitBodyp = new AstIf{
|
||||
flp,
|
||||
new AstEq{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 0}},
|
||||
makeStateTransition(flp, stateVarp, cntVarp, checkState, rangeWidth),
|
||||
makeStateTransition(flp, stateVarp, cntVarp, bounds[i].checkState,
|
||||
initCnt),
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}}};
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, waitState, bodyp);
|
||||
fsmChainp
|
||||
= chainState(flp, fsmChainp, stateVarp, bounds[i].waitState, waitBodyp);
|
||||
}
|
||||
|
||||
// CHECK_RANGE: check expr each cycle, fail on timeout
|
||||
{
|
||||
AstNode* matchActionp = nullptr;
|
||||
AstNode* const timeoutp = new AstBegin{flp, "", nullptr, true};
|
||||
VN_AS(timeoutp, Begin)
|
||||
->addStmtsp(new AstAssign{flp,
|
||||
new AstVarRef{flp, failVarp, VAccess::WRITE},
|
||||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
VN_AS(timeoutp, Begin)
|
||||
->addStmtsp(new AstAssign{flp,
|
||||
new AstVarRef{flp, stateVarp, VAccess::WRITE},
|
||||
new AstConst{flp, 0}});
|
||||
AstNode* const decrementp
|
||||
= new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}};
|
||||
// CHECK state: sample consequent each cycle
|
||||
AstNode* const matchActionp = makeOnMatchAction(flp, stateVarp, cntVarp, isTail,
|
||||
afterMatchState, nextStep.delay);
|
||||
AstNode* const checkBodyp
|
||||
= makeRangeCheckBody(flp, stateVarp, cntVarp, failVarp, nextStep.exprp,
|
||||
matchActionp, step.isUnbounded);
|
||||
fsmChainp
|
||||
= chainState(flp, fsmChainp, stateVarp, bounds[i].checkState, checkBodyp);
|
||||
|
||||
AstIf* const failOrRetryp
|
||||
= new AstIf{flp,
|
||||
new AstEq{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 0}},
|
||||
timeoutp, decrementp};
|
||||
|
||||
if (nextStep.delay > 0) {
|
||||
matchActionp = makeStateTransition(flp, stateVarp, cntVarp,
|
||||
afterMatchState, nextStep.delay - 1);
|
||||
} else {
|
||||
matchActionp
|
||||
= makeStateTransition(flp, stateVarp, cntVarp, afterMatchState, 0);
|
||||
}
|
||||
|
||||
AstIf* const checkp
|
||||
= new AstIf{flp, new AstSampled{flp, nextStep.exprp->cloneTree(false)},
|
||||
matchActionp, failOrRetryp};
|
||||
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, checkState, checkp);
|
||||
}
|
||||
|
||||
// Skip next step (already consumed as the range check target)
|
||||
++i;
|
||||
++i; // step[i+1] consumed as the CHECK target
|
||||
continue;
|
||||
|
||||
} else if (step.delay > 0) {
|
||||
// Fixed delay: count down then advance
|
||||
const int waitState = nextState++;
|
||||
// Fixed delay: count down then advance to next state
|
||||
const int nextStateNum = bounds[i].waitState + 1;
|
||||
AstNode* const bodyp = new AstIf{
|
||||
flp,
|
||||
new AstEq{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 0}},
|
||||
new AstAssign{flp, new AstVarRef{flp, stateVarp, VAccess::WRITE},
|
||||
new AstConst{flp, static_cast<uint32_t>(nextState)}},
|
||||
new AstConst{flp, static_cast<uint32_t>(nextStateNum)}},
|
||||
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||
new AstConst{flp, 1}}}};
|
||||
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, waitState, bodyp);
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, bounds[i].waitState, bodyp);
|
||||
|
||||
} else if (i == steps.size() - 1 && step.exprp) {
|
||||
// Tail: check final expression, pass or fail
|
||||
const int checkState = nextState++;
|
||||
// Tail: sample final expression, report pass/fail
|
||||
AstNode* const passp = new AstAssign{
|
||||
flp, new AstVarRef{flp, stateVarp, VAccess::WRITE}, new AstConst{flp, 0}};
|
||||
AstBegin* const failp = new AstBegin{flp, "", nullptr, true};
|
||||
|
|
@ -1019,27 +1119,17 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
new AstConst{flp, AstConst::BitTrue{}}});
|
||||
failp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, stateVarp, VAccess::WRITE},
|
||||
new AstConst{flp, 0}});
|
||||
|
||||
AstIf* const bodyp = new AstIf{
|
||||
flp, new AstSampled{flp, step.exprp->cloneTree(false)}, passp, failp};
|
||||
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, checkState, bodyp);
|
||||
fsmChainp = chainState(flp, fsmChainp, stateVarp, bounds[i].checkState, bodyp);
|
||||
}
|
||||
}
|
||||
|
||||
// Build IDLE state (state 0): check trigger and start
|
||||
// Build IDLE state (state 0)
|
||||
AstNode* idleBodyp = nullptr;
|
||||
const SeqStep& firstStep = steps[0];
|
||||
int initCnt = 0;
|
||||
if (firstStep.isRange) {
|
||||
initCnt = firstStep.rangeMin - 1;
|
||||
} else {
|
||||
initCnt = firstStep.delay - 1;
|
||||
}
|
||||
AstNode* const startActionp
|
||||
= makeStateTransition(flp, stateVarp, cntVarp, 1, initCnt < 0 ? 0 : initCnt);
|
||||
|
||||
// Trigger = antecedent (from implication) AND/OR first step expression
|
||||
// Trigger = antecedent AND/OR first step expression
|
||||
AstNodeExpr* triggerp = nullptr;
|
||||
if (antExprp && firstStep.exprp) {
|
||||
triggerp = new AstAnd{flp, new AstSampled{flp, antExprp->cloneTree(false)},
|
||||
|
|
@ -1050,12 +1140,37 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
triggerp = new AstSampled{flp, firstStep.exprp->cloneTree(false)};
|
||||
}
|
||||
|
||||
if (triggerp) {
|
||||
triggerp->dtypeSetBit();
|
||||
idleBodyp = new AstIf{flp, triggerp, startActionp, nullptr};
|
||||
if (firstStep.isUnbounded && firstStep.rangeMin == 0 && steps.size() > 1) {
|
||||
// ##[*] / ##[0:$]: check consequent immediately in IDLE.
|
||||
// On ##0 match: perform match action without entering CHECK.
|
||||
// On no match: enter CHECK (state bounds[0].checkState) to wait.
|
||||
const SeqStep& nextStep = steps[1];
|
||||
const int checkState = bounds[0].checkState;
|
||||
const int afterMatch = checkState + 1;
|
||||
const bool isTail = (steps.size() == 2 && nextStep.delay == 0);
|
||||
AstNodeExpr* const immCheckp = new AstSampled{flp, nextStep.exprp->cloneTree(false)};
|
||||
immCheckp->dtypeSetBit();
|
||||
AstNode* const immMatchp
|
||||
= makeOnMatchAction(flp, stateVarp, cntVarp, isTail, afterMatch, nextStep.delay);
|
||||
AstNode* const toCheckp = makeStateTransition(flp, stateVarp, cntVarp, checkState, 0);
|
||||
AstIf* const starBodyp = new AstIf{flp, immCheckp, immMatchp, toCheckp};
|
||||
if (triggerp) {
|
||||
triggerp->dtypeSetBit();
|
||||
idleBodyp = new AstIf{flp, triggerp, starBodyp, nullptr};
|
||||
} else {
|
||||
idleBodyp = starBodyp;
|
||||
}
|
||||
} else {
|
||||
// Unary form with no antecedent: start unconditionally each cycle
|
||||
idleBodyp = startActionp;
|
||||
// Standard start: transition to state 1 with appropriate counter
|
||||
int initCnt = firstStep.isRange ? firstStep.rangeMin - 1 : firstStep.delay - 1;
|
||||
AstNode* const startActionp
|
||||
= makeStateTransition(flp, stateVarp, cntVarp, 1, initCnt < 0 ? 0 : initCnt);
|
||||
if (triggerp) {
|
||||
triggerp->dtypeSetBit();
|
||||
idleBodyp = new AstIf{flp, triggerp, startActionp, nullptr};
|
||||
} else {
|
||||
idleBodyp = startActionp;
|
||||
}
|
||||
}
|
||||
|
||||
// Chain: if (state == 0) idle else if (state == 1) ... else ...
|
||||
|
|
@ -1168,16 +1283,18 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
AstVar* const stateVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__state",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
stateVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
stateVarp->noSample(true);
|
||||
AstVar* const cntVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__cnt",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
cntVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
cntVarp->noSample(true);
|
||||
AstVar* const failVarp = new AstVar{flp, VVarType::MODULETEMP, baseName + "__fail",
|
||||
nodep->findBasicDType(VBasicDTypeKwd::BIT)};
|
||||
failVarp->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
failVarp->noSample(true);
|
||||
|
||||
// Build FSM body
|
||||
AstNode* const fsmBodyp
|
||||
= buildFsmBody(flp, stateVarp, cntVarp, failVarp, steps, sensesp, antExprp);
|
||||
AstNode* const fsmBodyp = buildFsmBody(flp, stateVarp, cntVarp, failVarp, steps, antExprp);
|
||||
|
||||
// Create Always block for the FSM (same scheduling as assertion always blocks)
|
||||
AstAlways* const alwaysp = new AstAlways{
|
||||
|
|
@ -1210,6 +1327,40 @@ class RangeDelayExpander final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstSThroughout* nodep) override {
|
||||
// Reject throughout with range-delay sequences before FSM expansion
|
||||
// would silently lose per-tick enforcement (IEEE 1800-2023 16.9.9)
|
||||
if (AstSExpr* const sexprp = VN_CAST(nodep->rhsp(), SExpr)) {
|
||||
if (containsRangeDelay(sexprp)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: throughout with range delay sequence");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Reject throughout with nested throughout or goto repetition
|
||||
if (VN_IS(nodep->rhsp(), SThroughout) || VN_IS(nodep->rhsp(), SGotoRep)) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: throughout with complex sequence operator");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
// Reject throughout with temporal SAnd/SOr (containing SExpr = multi-cycle).
|
||||
// Pure boolean SAnd/SOr are OK -- AssertPropLowerVisitor lowers them to LogAnd/LogOr.
|
||||
if (VN_IS(nodep->rhsp(), SAnd) || VN_IS(nodep->rhsp(), SOr)) {
|
||||
bool hasSExpr = false;
|
||||
nodep->rhsp()->foreach([&](const AstSExpr*) { hasSExpr = true; });
|
||||
if (hasSExpr) {
|
||||
nodep->v3warn(E_UNSUPPORTED,
|
||||
"Unsupported: throughout with complex sequence operator");
|
||||
nodep->replaceWith(new AstConst{nodep->fileline(), AstConst::BitFalse{}});
|
||||
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
|
|
|
|||
|
|
@ -125,6 +125,8 @@ public:
|
|||
virtual AstNodeDType* subDTypep() const VL_MT_STABLE { return nullptr; }
|
||||
virtual AstNodeDType* subDType2p() const VL_MT_STABLE { return nullptr; }
|
||||
virtual bool isAggregateType() const { return false; }
|
||||
// True for unpacked, dynamic, queue, and associative arrays (not packed arrays)
|
||||
bool isNonPackedArray() const;
|
||||
virtual bool isFourstate() const;
|
||||
// Ideally an IEEE $typename
|
||||
virtual string prettyDTypeName(bool) const { return prettyTypeName(); }
|
||||
|
|
|
|||
|
|
@ -2197,22 +2197,6 @@ public:
|
|||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
int instrCount() const override { return widthInstrs(); }
|
||||
};
|
||||
class AstSExprGotoRep final : public AstNodeExpr {
|
||||
// Goto repetition: expr [-> count]
|
||||
// IEEE 1800-2023 16.9.2
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
public:
|
||||
explicit AstSExprGotoRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
: ASTGEN_SUPER_SExprGotoRep(fl) {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSExprGotoRep;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
};
|
||||
class AstSFormatArg final : public AstNodeExpr {
|
||||
// Information for formatting each argument to AstSFormat,
|
||||
// used to pass to (potentially) runtime decoding of format arguments
|
||||
|
|
@ -2315,6 +2299,22 @@ public:
|
|||
bool cleanOut() const override { V3ERROR_NA_RETURN(true); }
|
||||
bool isSystemFunc() const override { return true; }
|
||||
};
|
||||
class AstSGotoRep final : public AstNodeExpr {
|
||||
// Goto repetition: expr [-> count]
|
||||
// IEEE 1800-2023 16.9.2
|
||||
// @astgen op1 := exprp : AstNodeExpr
|
||||
// @astgen op2 := countp : AstNodeExpr
|
||||
public:
|
||||
explicit AstSGotoRep(FileLine* fl, AstNodeExpr* exprp, AstNodeExpr* countp)
|
||||
: ASTGEN_SUPER_SGotoRep(fl) {
|
||||
this->exprp(exprp);
|
||||
this->countp(countp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSGotoRep;
|
||||
string emitVerilog() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { V3ERROR_NA_RETURN(""); }
|
||||
};
|
||||
class AstSScanF final : public AstNodeExpr {
|
||||
// @astgen op1 := exprsp : List[AstNodeExpr] // VarRefs for results
|
||||
// @astgen op2 := fromp : AstNodeExpr
|
||||
|
|
@ -3721,6 +3721,28 @@ public:
|
|||
bool sizeMattersRhs() const override { return false; }
|
||||
int instrCount() const override { return widthInstrs() + INSTR_COUNT_BRANCH; }
|
||||
};
|
||||
class AstSThroughout final : public AstNodeBiop {
|
||||
// expr throughout seq (IEEE 1800-2023 16.9.9)
|
||||
public:
|
||||
AstSThroughout(FileLine* fl, AstNodeExpr* lhsp, AstNodeExpr* rhsp)
|
||||
: ASTGEN_SUPER_SThroughout(fl, lhsp, rhsp) {
|
||||
dtypeSetBit();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstSThroughout;
|
||||
// LCOV_EXCL_START // Lowered in V3AssertProp before these are called
|
||||
void numberOperate(V3Number& out, const V3Number& lhs, const V3Number& rhs) override {
|
||||
out.opLogAnd(lhs, rhs);
|
||||
}
|
||||
string emitVerilog() override { return "%k(%l %fthroughout %r)"; }
|
||||
string emitC() override { V3ERROR_NA_RETURN(""); }
|
||||
string emitSimpleOperator() override { V3ERROR_NA_RETURN(""); }
|
||||
bool cleanOut() const override { return true; }
|
||||
bool cleanLhs() const override { return true; }
|
||||
bool cleanRhs() const override { return true; }
|
||||
bool sizeMattersLhs() const override { return false; }
|
||||
bool sizeMattersRhs() const override { return false; }
|
||||
// LCOV_EXCL_STOP
|
||||
};
|
||||
class AstSel final : public AstNodeBiop {
|
||||
// *Resolved* (tyep checked) multiple bit range extraction. Always const width
|
||||
// @astgen alias op1 := fromp
|
||||
|
|
|
|||
|
|
@ -1951,6 +1951,7 @@ class AstVar final : public AstNode {
|
|||
bool m_noCReset : 1; // Do not do automated CReset creation
|
||||
bool m_noReset : 1; // Do not do automated reset/randomization
|
||||
bool m_noSubst : 1; // Do not substitute out references
|
||||
bool m_sampled : 1; // Sampled timing region
|
||||
bool m_substConstOnly : 1; // Only substitute if constant
|
||||
bool m_overridenParam : 1; // Overridden parameter by #(...) or defparam
|
||||
bool m_trace : 1; // Trace this variable
|
||||
|
|
@ -1969,6 +1970,7 @@ class AstVar final : public AstNode {
|
|||
bool m_globalConstrained : 1; // Global constraint per IEEE 1800-2023 18.5.8
|
||||
bool m_isStdRandomizeArg : 1; // Argument variable created for std::randomize (__Varg*)
|
||||
bool m_noSample : 1; // Do not wrap with AstSampled in assertion context
|
||||
bool m_processQueue : 1; // Process queue variable
|
||||
void init() {
|
||||
m_ansi = false;
|
||||
m_declTyped = false;
|
||||
|
|
@ -2009,6 +2011,7 @@ class AstVar final : public AstNode {
|
|||
m_noCReset = false;
|
||||
m_noReset = false;
|
||||
m_noSubst = false;
|
||||
m_sampled = false;
|
||||
m_substConstOnly = false;
|
||||
m_overridenParam = false;
|
||||
m_trace = false;
|
||||
|
|
@ -2027,6 +2030,7 @@ class AstVar final : public AstNode {
|
|||
m_globalConstrained = false;
|
||||
m_isStdRandomizeArg = false;
|
||||
m_noSample = false;
|
||||
m_processQueue = false;
|
||||
}
|
||||
|
||||
public:
|
||||
|
|
@ -2176,6 +2180,10 @@ public:
|
|||
void noSubst(bool flag) { m_noSubst = flag; }
|
||||
bool noSample() const { return m_noSample; }
|
||||
void noSample(bool flag) { m_noSample = flag; }
|
||||
bool processQueue() const { return m_processQueue; }
|
||||
void processQueue(bool flag) { m_processQueue = flag; }
|
||||
bool sampled() const { return m_sampled; }
|
||||
void sampled(bool flag) { m_sampled = flag; }
|
||||
bool substConstOnly() const { return m_substConstOnly; }
|
||||
void substConstOnly(bool flag) { m_substConstOnly = flag; }
|
||||
bool overriddenParam() const { return m_overridenParam; }
|
||||
|
|
|
|||
|
|
@ -544,6 +544,7 @@ class AstDelay final : public AstNodeStmt {
|
|||
// @astgen op1 := lhsp : AstNodeExpr // Delay value (or min for range)
|
||||
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
|
||||
// @astgen op3 := rhsp : Optional[AstNodeExpr] // Max delay value (range delay only)
|
||||
// @astgen op4 := throughoutp : Optional[AstNodeExpr] // Throughout condition (IEEE 16.9.9)
|
||||
VTimescale m_timeunit; // Delay's time unit
|
||||
const bool m_isCycle; // True if it is a cycle delay
|
||||
|
||||
|
|
@ -562,6 +563,7 @@ public:
|
|||
VTimescale timeunit() const { return m_timeunit; }
|
||||
bool isCycleDelay() const { return m_isCycle; }
|
||||
bool isRangeDelay() const { return rhsp() != nullptr; }
|
||||
bool isUnbounded() const { return rhsp() && VN_IS(rhsp(), Unbounded); }
|
||||
};
|
||||
class AstDisable final : public AstNodeStmt {
|
||||
// @astgen op1 := targetRefp : Optional[AstNodeExpr] // Reference to link in V3LinkDot
|
||||
|
|
|
|||
|
|
@ -1021,6 +1021,11 @@ bool AstNodeDType::similarDType(const AstNodeDType* samep) const {
|
|||
|
||||
bool AstNodeDType::isFourstate() const { return basicp() && basicp()->isFourstate(); }
|
||||
|
||||
bool AstNodeDType::isNonPackedArray() const {
|
||||
return VN_IS(this, UnpackArrayDType) || VN_IS(this, DynArrayDType) || VN_IS(this, QueueDType)
|
||||
|| VN_IS(this, AssocArrayDType);
|
||||
}
|
||||
|
||||
class AstNodeDType::CTypeRecursed final {
|
||||
public:
|
||||
string m_type; // The base type, e.g.: "Foo_t"s
|
||||
|
|
@ -2964,6 +2969,8 @@ void AstVar::dump(std::ostream& str) const {
|
|||
if (rand().isRandomizable()) str << " [" << rand() << "]";
|
||||
if (noCReset()) str << " [!CRST]";
|
||||
if (noReset()) str << " [!RST]";
|
||||
if (processQueue()) str << " [PROCQ]";
|
||||
if (sampled()) str << " [SAMPLED]";
|
||||
if (attrIsolateAssign()) str << " [aISO]";
|
||||
if (attrFileDescr()) str << " [aFD]";
|
||||
if (isFuncReturn()) {
|
||||
|
|
@ -2994,6 +3001,8 @@ void AstVar::dumpJson(std::ostream& str) const {
|
|||
dumpJsonBoolFuncIf(str, isUsedLoopIdx);
|
||||
dumpJsonBoolFuncIf(str, noCReset);
|
||||
dumpJsonBoolFuncIf(str, noReset);
|
||||
dumpJsonBoolFuncIf(str, processQueue);
|
||||
dumpJsonBoolFuncIf(str, sampled);
|
||||
dumpJsonBoolFuncIf(str, attrIsolateAssign);
|
||||
dumpJsonBoolFuncIf(str, attrFileDescr);
|
||||
dumpJsonBoolFuncIf(str, isDpiOpenArray);
|
||||
|
|
|
|||
|
|
@ -100,7 +100,7 @@ class ClockVisitor final : public VNVisitor {
|
|||
void visit(AstVarScope* nodep) override {
|
||||
AstVar* const varp = nodep->varp();
|
||||
if (!varp->valuep()) return;
|
||||
if (!VString::startsWith(varp->name(), "__Vsampled")) return;
|
||||
if (!varp->sampled()) return;
|
||||
|
||||
// Create the containing function on first encounter
|
||||
if (!m_sampleCFuncp) {
|
||||
|
|
|
|||
|
|
@ -781,7 +781,12 @@ void DfgVertex::typeCheck(const DfgGraph& dfg) const {
|
|||
}
|
||||
|
||||
case VDfgType::SAnd:
|
||||
case VDfgType::SOr: UASSERT_OBJ(false, this, "SAnd/SOr should be removed before DFG"); return;
|
||||
case VDfgType::SOr:
|
||||
case VDfgType::SThroughout: {
|
||||
UASSERT_OBJ(false, this,
|
||||
"SAnd/SOr/SThroughout should be removed before DFG"); // LCOV_EXCL_LINE
|
||||
return; // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
case VDfgType::LogAnd:
|
||||
case VDfgType::LogEq:
|
||||
|
|
|
|||
|
|
@ -127,6 +127,7 @@ class V3DfgCse final {
|
|||
case VDfgType::StreamR:
|
||||
case VDfgType::SAnd:
|
||||
case VDfgType::SOr:
|
||||
case VDfgType::SThroughout:
|
||||
case VDfgType::Sub:
|
||||
case VDfgType::Xor: return V3Hash{};
|
||||
}
|
||||
|
|
@ -251,6 +252,7 @@ class V3DfgCse final {
|
|||
case VDfgType::StreamL:
|
||||
case VDfgType::SAnd:
|
||||
case VDfgType::SOr:
|
||||
case VDfgType::SThroughout:
|
||||
case VDfgType::StreamR:
|
||||
case VDfgType::Sub:
|
||||
case VDfgType::Xor: return true;
|
||||
|
|
|
|||
|
|
@ -1517,7 +1517,13 @@ public:
|
|||
void visit(AstMemberSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
putnbs(nodep, "->");
|
||||
puts(nodep->varp()->nameProtect());
|
||||
if (nodep->varp()->isIfaceRef()) {
|
||||
// varp is the __Viftop companion (e.g. "tx__Viftop"); use the
|
||||
// MemberSel name which matches the cell's C++ member (e.g. "tx").
|
||||
puts(nodep->nameProtect());
|
||||
} else {
|
||||
puts(nodep->varp()->nameProtect());
|
||||
}
|
||||
}
|
||||
void visit(AstStructSel* nodep) override {
|
||||
iterateAndNextConstNull(nodep->fromp());
|
||||
|
|
|
|||
|
|
@ -280,9 +280,7 @@ class EmitCHeader final : public EmitCConstInit {
|
|||
enum class AttributeType { Width, Dimension };
|
||||
// Get member attribute based on type
|
||||
int getNodeAttribute(const AstMemberDType* itemp, AttributeType type) {
|
||||
const bool isArrayType
|
||||
= VN_IS(itemp->dtypep(), UnpackArrayDType) || VN_IS(itemp->dtypep(), DynArrayDType)
|
||||
|| VN_IS(itemp->dtypep(), QueueDType) || VN_IS(itemp->dtypep(), AssocArrayDType);
|
||||
const bool isArrayType = itemp->dtypep()->isNonPackedArray();
|
||||
switch (type) {
|
||||
case AttributeType::Width: {
|
||||
if (isArrayType) {
|
||||
|
|
|
|||
|
|
@ -1067,7 +1067,6 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
|||
}
|
||||
iterateConst(nodep->exprp());
|
||||
}
|
||||
|
||||
// Terminals
|
||||
void visit(AstVarRef* nodep) override {
|
||||
if (nodep->varScopep()) {
|
||||
|
|
|
|||
|
|
@ -611,7 +611,7 @@ class ForkVisitor final : public VNVisitor {
|
|||
const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
|
||||
if (!methodp || methodp->name() != "push_back") return false;
|
||||
const AstVarRef* const queueRefp = VN_CAST(methodp->fromp(), VarRef);
|
||||
return queueRefp && queueRefp->name().rfind("__VprocessQueue_", 0) == 0;
|
||||
return queueRefp && queueRefp->varp()->processQueue();
|
||||
}
|
||||
static void moveForkSentinelAfterDisableQueuePushes(AstBegin* const beginp) {
|
||||
AstNode* const firstStmtp = beginp->stmtsp();
|
||||
|
|
|
|||
|
|
@ -236,20 +236,24 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
m_taskDisableBegins.emplace(taskp, taskBodyp);
|
||||
return taskBodyp;
|
||||
}
|
||||
AstVar* getOrCreateTaskDisableQueuep(AstTask* const taskp, FileLine* const fl) {
|
||||
const auto it = m_taskDisableQueues.find(taskp);
|
||||
if (it != m_taskDisableQueues.end()) return it->second;
|
||||
|
||||
AstVar* getProcessQueuep(AstNode* const nodep, FileLine* const fl) {
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(taskp->name()), VFlagChildDType{},
|
||||
fl, VVarType::VAR, m_queueNames.get(nodep->name()), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
processQueuep->processQueue(true);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
return processQueuep;
|
||||
}
|
||||
AstVar* getOrCreateTaskDisableQueuep(AstTask* const taskp, FileLine* const fl) {
|
||||
const auto it = m_taskDisableQueues.find(taskp);
|
||||
if (it != m_taskDisableQueues.end()) return it->second;
|
||||
|
||||
AstVar* const processQueuep = getProcessQueuep(taskp, fl);
|
||||
AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(fl, processQueuep);
|
||||
AstBegin* const taskBodyp = getOrCreateTaskDisableBeginp(taskp, fl);
|
||||
prependStmtsp(taskBodyp, pushCurrentProcessp);
|
||||
|
|
@ -274,16 +278,7 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
const auto it = m_beginDisableQueues.find(beginp);
|
||||
if (it != m_beginDisableQueues.end()) return it->second;
|
||||
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(beginp->name()), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
|
||||
AstVar* const processQueuep = getProcessQueuep(beginp, fl);
|
||||
AstStmtExpr* const pushCurrentProcessp = getQueuePushProcessSelfp(fl, processQueuep);
|
||||
AstBegin* const beginBodyp = getOrCreateBeginDisableBeginp(beginp, fl);
|
||||
prependStmtsp(beginBodyp, pushCurrentProcessp);
|
||||
|
|
@ -315,17 +310,9 @@ class LinkJumpVisitor final : public VNVisitor {
|
|||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: disabling fork from task / function");
|
||||
}
|
||||
}
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstClass* const processClassp
|
||||
= VN_AS(getMemberp(v3Global.rootp()->stdPackagep(), "process"), Class);
|
||||
// Declare queue of processes (as a global variable for simplicity)
|
||||
AstVar* const processQueuep = new AstVar{
|
||||
fl, VVarType::VAR, m_queueNames.get(targetp->name()), VFlagChildDType{},
|
||||
new AstQueueDType{fl, VFlagChildDType{},
|
||||
new AstClassRefDType{fl, processClassp, nullptr}, nullptr}};
|
||||
processQueuep->lifetime(VLifetime::STATIC_EXPLICIT);
|
||||
topPkgp->addStmtsp(processQueuep);
|
||||
|
||||
AstPackage* const topPkgp = v3Global.rootp()->dollarUnitPkgAddp();
|
||||
AstVar* const processQueuep = getProcessQueuep(targetp, fl);
|
||||
AstVarRef* const queueWriteRefp
|
||||
= new AstVarRef{fl, topPkgp, processQueuep, VAccess::WRITE};
|
||||
AstStmtExpr* pushCurrentProcessp = getQueuePushProcessSelfp(queueWriteRefp);
|
||||
|
|
|
|||
|
|
@ -69,6 +69,13 @@ class LinkLValueVisitor final : public VNVisitor {
|
|||
}
|
||||
if (m_setForcedByCode) {
|
||||
nodep->varp()->setForcedByCode();
|
||||
// If a public signal is being forced in SystemVerilog and VPI
|
||||
// is enabled, mark it as forceable to ensure that the VPI
|
||||
// functions read the forced value correctly
|
||||
if (v3Global.opt.vpi()
|
||||
&& (nodep->varp()->isSigPublic() || nodep->varp()->isSigModPublic())) {
|
||||
nodep->varp()->setForceable();
|
||||
}
|
||||
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {
|
||||
// This is allowed with IEEE 1800-2009 module input with default value.
|
||||
// the checking now happens in V3Width::visit(AstNodeVarRef*)
|
||||
|
|
|
|||
|
|
@ -918,6 +918,17 @@ class LinkParseVisitor final : public VNVisitor {
|
|||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstCaseItem* nodep) override {
|
||||
// Move default caseItems to the bottom of the list
|
||||
// That saves us from having to search each case list twice, for non-defaults and defaults
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
|
||||
nodep->user2(true);
|
||||
AstNode* const nextp = nodep->nextp();
|
||||
nodep->unlinkFrBack();
|
||||
nextp->addNext(nodep);
|
||||
}
|
||||
}
|
||||
void visit(AstDot* nodep) override {
|
||||
cleanFileline(nodep);
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -47,19 +47,16 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
// Below state needs to be preserved between each module call.
|
||||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstClass* m_classp = nullptr; // Class we're inside
|
||||
string m_randcIllegalWhy; // Why randc illegal
|
||||
AstNode* m_randcIllegalp = nullptr; // Node causing randc illegal
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Function or task we're inside
|
||||
int m_senitemCvtNum = 0; // Temporary signal counter
|
||||
std::deque<AstGenFor*> m_underGenFors; // Stack of GenFor underneath
|
||||
bool m_underGenerate = false; // Under GenFor/GenIf
|
||||
AstNodeExpr* m_currentRandomizeSelectp = nullptr; // fromp() of current `randomize()` call
|
||||
bool m_inRandomizeWith = false; // If in randomize() with (and no other with afterwards)
|
||||
|
||||
// VISITORS
|
||||
// TODO: Most of these visitors are here for historical reasons.
|
||||
// TODO: ExpectDescriptor can move to data type resolution, and the rest
|
||||
// TODO: could move to V3LinkParse to get them out of the way of elaboration
|
||||
// TODO: Some also can move to V3LinkWidth, to happen once post-LinkDot
|
||||
void visit(AstNodeModule* nodep) override {
|
||||
// Module: Create sim table for entire module and iterate
|
||||
UINFO(8, "MODULE " << nodep);
|
||||
|
|
@ -80,39 +77,6 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstConstraint* nodep) override {
|
||||
// V3LinkDot moved the isExternDef into the class, the extern proto was
|
||||
// checked to exist, and now isn't needed
|
||||
nodep->isExternDef(false);
|
||||
if (nodep->isExternProto()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstConstraintBefore* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
m_randcIllegalWhy = "'solve before' (IEEE 1800-2023 18.5.9)";
|
||||
m_randcIllegalp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstDist* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
m_randcIllegalWhy = "'constraint dist' (IEEE 1800-2023 18.5.3)";
|
||||
m_randcIllegalp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstConstraintExpr* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
if (nodep->isSoft()) {
|
||||
m_randcIllegalWhy = "'constraint soft' (IEEE 1800-2023 18.5.13.1)";
|
||||
m_randcIllegalp = nodep;
|
||||
}
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
void visit(AstInitialAutomatic* nodep) override {
|
||||
iterateChildren(nodep);
|
||||
|
|
@ -151,14 +115,6 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
<< nodep->prettyNameQ()
|
||||
<< " used outside generate for loop (IEEE 1800-2023 27.4)");
|
||||
}
|
||||
if (nodep->varp()->isRandC() && m_randcIllegalp) {
|
||||
nodep->v3error("Randc variables not allowed in "
|
||||
<< m_randcIllegalWhy << '\n'
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< m_randcIllegalp->warnOther()
|
||||
<< "... Location of restricting expression\n"
|
||||
<< m_randcIllegalp->warnContextSecondary());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
|
@ -196,22 +152,6 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
if (nodep->dpiExport()) nodep->scopeNamep(new AstScopeName{nodep->fileline(), false});
|
||||
}
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
VL_RESTORER(m_currentRandomizeSelectp);
|
||||
if (nodep->taskp()) {
|
||||
if (AstSequence* const seqp = VN_CAST(nodep->taskp(), Sequence))
|
||||
seqp->isReferenced(true);
|
||||
}
|
||||
|
||||
if (nodep->name() == "randomize") {
|
||||
if (const AstMethodCall* const methodcallp = VN_CAST(nodep, MethodCall)) {
|
||||
if (m_inRandomizeWith) {
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: randomize() nested in inline randomize() constraints");
|
||||
}
|
||||
m_currentRandomizeSelectp = methodcallp->fromp();
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
if (AstLet* letp = VN_CAST(nodep->taskp(), Let)) {
|
||||
UINFO(7, "letSubstitute() " << nodep << " <- " << letp);
|
||||
|
|
@ -260,18 +200,6 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
}
|
||||
|
||||
void visit(AstCaseItem* nodep) override {
|
||||
// Move default caseItems to the bottom of the list
|
||||
// That saves us from having to search each case list twice, for non-defaults and defaults
|
||||
iterateChildren(nodep);
|
||||
if (!nodep->user2() && nodep->isDefault() && nodep->nextp()) {
|
||||
nodep->user2(true);
|
||||
AstNode* const nextp = nodep->nextp();
|
||||
nodep->unlinkFrBack();
|
||||
nextp->addNext(nodep);
|
||||
}
|
||||
}
|
||||
|
||||
void visit(AstLet* nodep) override {
|
||||
// Lets have been (or about to be) substituted, we can remove
|
||||
nodep->unlinkFrBack();
|
||||
|
|
@ -528,30 +456,6 @@ class LinkResolveVisitor final : public VNVisitor {
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_inRandomizeWith && nodep->fromp()->isSame(m_currentRandomizeSelectp)) {
|
||||
// Replace member selects to the element
|
||||
// on which the randomize() is called with LambdaArgRef
|
||||
// This allows V3Randomize to work properly when
|
||||
// constrained variables are referred using that object
|
||||
AstNodeExpr* const prevFromp = nodep->fromp();
|
||||
prevFromp->replaceWith(
|
||||
new AstLambdaArgRef{prevFromp->fileline(), prevFromp->name(), false});
|
||||
pushDeletep(prevFromp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstWith* nodep) override {
|
||||
VL_RESTORER(m_inRandomizeWith);
|
||||
if (const AstMethodCall* const methodCallp = VN_CAST(nodep->backp(), MethodCall)) {
|
||||
m_inRandomizeWith = methodCallp->name() == "randomize";
|
||||
} else {
|
||||
m_inRandomizeWith = false;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
|
|
@ -594,7 +498,7 @@ public:
|
|||
};
|
||||
|
||||
//######################################################################
|
||||
// Link class functions
|
||||
// V3LinkResolve class functions
|
||||
|
||||
void V3LinkResolve::linkResolve(AstNetlist* rootp) {
|
||||
UINFO(4, __FUNCTION__ << ": ");
|
||||
|
|
|
|||
|
|
@ -0,0 +1,149 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Resolve module/signal name references
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// 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-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
// LinkResolve TRANSFORMATIONS:
|
||||
// Top-down traversal
|
||||
// With vars: Fixup LambdaRefs
|
||||
//*************************************************************************
|
||||
|
||||
#include "V3PchAstNoMT.h" // VL_MT_DISABLED_CODE_UNIT
|
||||
|
||||
#include "V3LinkWith.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
//######################################################################
|
||||
// Link state, as a visitor of each AstNode
|
||||
|
||||
class LinkWithVisitor final : public VNVisitor {
|
||||
// NODE STATE
|
||||
|
||||
// STATE
|
||||
// Below state needs to be preserved between each module call.
|
||||
string m_randcIllegalWhy; // Why randc illegal
|
||||
AstNode* m_randcIllegalp = nullptr; // Node causing randc illegal
|
||||
AstNodeExpr* m_currentRandomizeSelectp = nullptr; // fromp() of current `randomize()` call
|
||||
bool m_inRandomizeWith = false; // If in randomize() with (and no other with afterwards)
|
||||
|
||||
// VISITORS
|
||||
void visit(AstConstraint* nodep) override {
|
||||
// V3LinkDot moved the isExternDef into the class, the extern proto was
|
||||
// checked to exist, and now isn't needed
|
||||
nodep->isExternDef(false);
|
||||
if (nodep->isExternProto()) {
|
||||
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||
return;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstConstraintBefore* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
m_randcIllegalWhy = "'solve before' (IEEE 1800-2023 18.5.9)";
|
||||
m_randcIllegalp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstDist* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
m_randcIllegalWhy = "'constraint dist' (IEEE 1800-2023 18.5.3)";
|
||||
m_randcIllegalp = nodep;
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
void visit(AstConstraintExpr* nodep) override {
|
||||
VL_RESTORER(m_randcIllegalWhy);
|
||||
VL_RESTORER(m_randcIllegalp);
|
||||
if (nodep->isSoft()) {
|
||||
m_randcIllegalWhy = "'constraint soft' (IEEE 1800-2023 18.5.13.1)";
|
||||
m_randcIllegalp = nodep;
|
||||
}
|
||||
iterateChildrenConst(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNodeVarRef* nodep) override {
|
||||
if (nodep->varp()) { // Else due to dead code, might not have var pointer
|
||||
if (nodep->varp()->isRandC() && m_randcIllegalp) {
|
||||
nodep->v3error("Randc variables not allowed in "
|
||||
<< m_randcIllegalWhy << '\n'
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
<< m_randcIllegalp->warnOther()
|
||||
<< "... Location of restricting expression\n"
|
||||
<< m_randcIllegalp->warnContextSecondary());
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNodeFTaskRef* nodep) override {
|
||||
VL_RESTORER(m_currentRandomizeSelectp);
|
||||
if (nodep->taskp()) {
|
||||
if (AstSequence* const seqp = VN_CAST(nodep->taskp(), Sequence))
|
||||
seqp->isReferenced(true);
|
||||
}
|
||||
|
||||
if (nodep->name() == "randomize") {
|
||||
if (const AstMethodCall* const methodcallp = VN_CAST(nodep, MethodCall)) {
|
||||
if (m_inRandomizeWith) {
|
||||
nodep->v3warn(
|
||||
E_UNSUPPORTED,
|
||||
"Unsupported: randomize() nested in inline randomize() constraints");
|
||||
}
|
||||
m_currentRandomizeSelectp = methodcallp->fromp();
|
||||
}
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstMemberSel* nodep) override {
|
||||
if (m_inRandomizeWith && nodep->fromp()->isSame(m_currentRandomizeSelectp)) {
|
||||
// Replace member selects to the element
|
||||
// on which the randomize() is called with LambdaArgRef
|
||||
// This allows V3Randomize to work properly when
|
||||
// constrained variables are referred using that object
|
||||
AstNodeExpr* const prevFromp = nodep->fromp();
|
||||
AstNodeExpr* const newp
|
||||
= new AstLambdaArgRef{prevFromp->fileline(), prevFromp->name(), false};
|
||||
prevFromp->replaceWith(newp);
|
||||
pushDeletep(prevFromp);
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstWith* nodep) override {
|
||||
VL_RESTORER(m_inRandomizeWith);
|
||||
if (const AstMethodCall* const methodCallp = VN_CAST(nodep->backp(), MethodCall)) {
|
||||
m_inRandomizeWith = methodCallp->name() == "randomize";
|
||||
} else {
|
||||
m_inRandomizeWith = false;
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
|
||||
void visit(AstNode* nodep) override { iterateChildren(nodep); }
|
||||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit LinkWithVisitor(AstNetlist* rootp) { iterate(rootp); }
|
||||
~LinkWithVisitor() override = default;
|
||||
};
|
||||
|
||||
//######################################################################
|
||||
// V3LinkWith class functions
|
||||
|
||||
void V3LinkWith::linkWith(AstNetlist* rootp) {
|
||||
UINFO(4, __FUNCTION__ << ": ");
|
||||
{ const LinkWithVisitor visitor{rootp}; } // Destruct before checking
|
||||
V3Global::dumpCheckGlobalTree("linkwith", 0, dumpTreeEitherLevel() >= 6);
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
// -*- mode: C++; c-file-style: "cc-mode" -*-
|
||||
//*************************************************************************
|
||||
// DESCRIPTION: Verilator: Link modules/signals together
|
||||
//
|
||||
// Code available from: https://verilator.org
|
||||
//
|
||||
//*************************************************************************
|
||||
//
|
||||
// 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-FileCopyrightText: 2003-2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
//
|
||||
//*************************************************************************
|
||||
|
||||
#ifndef VERILATOR_V3LINKWITH_H_
|
||||
#define VERILATOR_V3LINKWITH_H_
|
||||
|
||||
#include "config_build.h"
|
||||
#include "verilatedos.h"
|
||||
|
||||
class AstNetlist;
|
||||
|
||||
//============================================================================
|
||||
|
||||
class V3LinkWith final {
|
||||
public:
|
||||
static void linkWith(AstNetlist* rootp) VL_MT_DISABLED;
|
||||
};
|
||||
|
||||
#endif // Guard
|
||||
|
|
@ -665,6 +665,7 @@ public:
|
|||
void isSigned(bool ssigned) { m_data.m_signed = ssigned; }
|
||||
bool isDouble() const VL_MT_SAFE { return dataType() == V3NumberDataType::DOUBLE; }
|
||||
bool isString() const VL_MT_SAFE { return dataType() == V3NumberDataType::STRING; }
|
||||
bool isOpaque() const VL_MT_SAFE { return isDouble() || isString(); }
|
||||
bool isNumber() const VL_MT_SAFE {
|
||||
return m_data.type() == V3NumberDataType::LOGIC
|
||||
|| m_data.type() == V3NumberDataType::DOUBLE;
|
||||
|
|
|
|||
|
|
@ -1261,6 +1261,31 @@ class ParamProcessor final {
|
|||
return result;
|
||||
}
|
||||
|
||||
static bool paramConstsEqualAtMaxWidth(AstConst* exprp, AstConst* origp) {
|
||||
// Return true if two integer constants are equal after sign-extending
|
||||
// both to max(width). A typed parameter default (e.g. byte) is
|
||||
// narrower than a 32-bit pin expression, so sameTree/areSame fail.
|
||||
if (exprp->num().width() == origp->num().width()) return false;
|
||||
if (exprp->num().isOpaque()) return false;
|
||||
if (origp->num().isOpaque()) return false;
|
||||
const int maxWidth = std::max(exprp->num().width(), origp->num().width());
|
||||
V3Number exprNum{exprp, maxWidth};
|
||||
if (exprp->num().isSigned()) {
|
||||
exprNum.opExtendS(exprp->num(), exprp->num().width());
|
||||
} else {
|
||||
exprNum.opAssign(exprp->num());
|
||||
}
|
||||
V3Number origNum{origp, maxWidth};
|
||||
if (origp->num().isSigned()) {
|
||||
origNum.opExtendS(origp->num(), origp->num().width());
|
||||
} else {
|
||||
origNum.opAssign(origp->num());
|
||||
}
|
||||
V3Number isEq{exprp, 1};
|
||||
isEq.opEq(exprNum, origNum);
|
||||
return isEq.isNeqZero();
|
||||
}
|
||||
|
||||
void cellPinCleanup(AstNode* nodep, AstPin* pinp, AstNodeModule* srcModp, string& longnamer,
|
||||
bool& any_overridesr) {
|
||||
if (!pinp->exprp()) return; // No-connect
|
||||
|
|
@ -1284,6 +1309,13 @@ class ParamProcessor final {
|
|||
} else {
|
||||
UINFO(9, "cellPinCleanup: before constify " << pinp << " " << modvarp);
|
||||
V3Const::constifyParamsEdit(pinp->exprp());
|
||||
// Cast/CastSize default values are not yet folded by V3Width.
|
||||
// Constify here so the comparison below sees a Const node.
|
||||
// Other node kinds are handled in the branches above.
|
||||
if (modvarp->valuep()
|
||||
&& (VN_IS(modvarp->valuep(), Cast) || VN_IS(modvarp->valuep(), CastSize))) {
|
||||
V3Const::constifyParamsEdit(modvarp->valuep());
|
||||
}
|
||||
UINFO(9, "cellPinCleanup: after constify " << pinp);
|
||||
// String constants are parsed as logic arrays and converted to strings in V3Const.
|
||||
// At this moment, some constants may have been already converted.
|
||||
|
|
@ -1309,7 +1341,8 @@ class ParamProcessor final {
|
|||
} else if (origp
|
||||
&& (exprp->sameTree(origp)
|
||||
|| (exprp->num().width() == origp->num().width()
|
||||
&& ParameterizedHierBlocks::areSame(exprp, origp)))) {
|
||||
&& ParameterizedHierBlocks::areSame(exprp, origp))
|
||||
|| paramConstsEqualAtMaxWidth(exprp, origp))) {
|
||||
// Setting parameter to its default value. Just ignore it.
|
||||
// This prevents making additional modules, and makes coverage more
|
||||
// obvious as it won't show up under a unique module page name.
|
||||
|
|
|
|||
|
|
@ -1136,8 +1136,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstClassRefDType* elemClassRefDtp = nullptr;
|
||||
{
|
||||
AstNodeDType* varDtp = varp->dtypep()->skipRefp();
|
||||
if (VN_IS(varDtp, DynArrayDType) || VN_IS(varDtp, QueueDType)
|
||||
|| VN_IS(varDtp, UnpackArrayDType) || VN_IS(varDtp, AssocArrayDType)) {
|
||||
if (varDtp->isNonPackedArray()) {
|
||||
AstNodeDType* const elemDtp = varDtp->subDTypep()->skipRefp();
|
||||
elemClassRefDtp = VN_CAST(elemDtp, ClassRefDType);
|
||||
if (elemClassRefDtp) {
|
||||
|
|
@ -1209,9 +1208,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
AstVar* const memberVarp = VN_CAST(mnodep, Var);
|
||||
if (!memberVarp || !memberVarp->rand().isRandomizable()) continue;
|
||||
AstNodeDType* const memberDtp = memberVarp->dtypep()->skipRefp();
|
||||
if (VN_IS(memberDtp, ClassRefDType) || VN_IS(memberDtp, DynArrayDType)
|
||||
|| VN_IS(memberDtp, QueueDType) || VN_IS(memberDtp, UnpackArrayDType)
|
||||
|| VN_IS(memberDtp, AssocArrayDType))
|
||||
if (VN_IS(memberDtp, ClassRefDType) || memberDtp->isNonPackedArray())
|
||||
continue;
|
||||
const int memberWidth = memberDtp->width();
|
||||
|
||||
|
|
@ -1281,9 +1278,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
VAccess::READWRITE},
|
||||
VCMethod::RANDOMIZER_WRITE_VAR};
|
||||
uint32_t dimension = 0;
|
||||
if (VN_IS(varp->dtypep(), UnpackArrayDType) || VN_IS(varp->dtypep(), DynArrayDType)
|
||||
|| VN_IS(varp->dtypep(), QueueDType)
|
||||
|| VN_IS(varp->dtypep(), AssocArrayDType)) {
|
||||
if (varp->dtypep()->isNonPackedArray()) {
|
||||
const std::pair<uint32_t, uint32_t> dims
|
||||
= varp->dtypep()->dimensions(/*includeBasic=*/true);
|
||||
const uint32_t unpackedDimensions = dims.second;
|
||||
|
|
@ -1316,9 +1311,7 @@ class ConstraintExprVisitor final : public VNVisitor {
|
|||
methodp->addPinsp(varRefp);
|
||||
}
|
||||
AstNodeDType* tmpDtypep = varp->dtypep();
|
||||
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|
||||
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
|
||||
tmpDtypep = tmpDtypep->subDTypep();
|
||||
while (tmpDtypep->isNonPackedArray()) tmpDtypep = tmpDtypep->subDTypep();
|
||||
const size_t width = tmpDtypep->width();
|
||||
methodp->addPinsp(
|
||||
new AstConst{varp->dtypep()->fileline(), AstConst::Unsized64{}, width});
|
||||
|
|
@ -3370,8 +3363,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
dtypep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||
};
|
||||
AstNodeExpr* tempElementp = nullptr;
|
||||
while (VN_IS(tempDTypep, DynArrayDType) || VN_IS(tempDTypep, UnpackArrayDType)
|
||||
|| VN_IS(tempDTypep, AssocArrayDType) || VN_IS(tempDTypep, QueueDType)) {
|
||||
while (tempDTypep->isNonPackedArray()) {
|
||||
AstVar* const newRandLoopIndxp = createLoopIndex(tempDTypep);
|
||||
randLoopIndxp = AstNode::addNext(randLoopIndxp, newRandLoopIndxp);
|
||||
AstNodeExpr* const tempExprp = tempElementp ? tempElementp : exprp;
|
||||
|
|
@ -4397,9 +4389,7 @@ class RandomizeVisitor final : public VNVisitor {
|
|||
}
|
||||
|
||||
AstNodeDType* tmpDtypep = arrVarp->dtypep();
|
||||
while (VN_IS(tmpDtypep, UnpackArrayDType) || VN_IS(tmpDtypep, DynArrayDType)
|
||||
|| VN_IS(tmpDtypep, QueueDType) || VN_IS(tmpDtypep, AssocArrayDType))
|
||||
tmpDtypep = tmpDtypep->subDTypep();
|
||||
while (tmpDtypep->isNonPackedArray()) tmpDtypep = tmpDtypep->subDTypep();
|
||||
const size_t width = tmpDtypep->width();
|
||||
|
||||
methodp->addPinsp(new AstConst{fl, AstConst::Unsized64{}, width});
|
||||
|
|
|
|||
|
|
@ -53,6 +53,7 @@ class SampledVisitor final : public VNVisitor {
|
|||
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
|
||||
newvarp->direction(VDirection::INPUT); // Inform V3Sched that it will be driven later
|
||||
newvarp->primaryIO(true);
|
||||
newvarp->sampled(true);
|
||||
vscp->user1p(newvscp);
|
||||
m_scopep->addVarsp(newvscp);
|
||||
// At the top of _eval, assign them (use valuep here as temporary storage during V3Sched)
|
||||
|
|
|
|||
203
src/V3Simulate.h
203
src/V3Simulate.h
|
|
@ -521,24 +521,23 @@ private:
|
|||
}
|
||||
}
|
||||
}
|
||||
if (!m_checkOnly && optimizable()) { // simulating
|
||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
||||
"LHS varref should be handled in AstAssign visitor.");
|
||||
{
|
||||
// Return simulation value - copy by reference instead of value for speed
|
||||
AstNodeExpr* valuep = fetchValueNull(vscp);
|
||||
if (!valuep) {
|
||||
if (m_params) {
|
||||
clearOptimizable(
|
||||
nodep, "Language violation: reference to non-function-local variable");
|
||||
} else {
|
||||
nodep->v3fatalSrc(
|
||||
"Variable value should have been set before any visitor called.");
|
||||
}
|
||||
valuep = allocConst(nodep); // Any value; just so recover from error
|
||||
if (m_checkOnly || !optimizable()) return; // Not simulating
|
||||
UASSERT_OBJ(nodep->access().isReadOnly(), nodep,
|
||||
"LHS varref should be handled in AstAssign visitor.");
|
||||
{
|
||||
// Return simulation value - copy by reference instead of value for speed
|
||||
AstNodeExpr* valuep = fetchValueNull(vscp);
|
||||
if (!valuep) {
|
||||
if (m_params) {
|
||||
clearOptimizable(
|
||||
nodep, "Language violation: reference to non-function-local variable");
|
||||
} else {
|
||||
nodep->v3fatalSrc(
|
||||
"Variable value should have been set before any visitor called.");
|
||||
}
|
||||
setValue(nodep, valuep);
|
||||
valuep = allocConst(nodep); // Any value; just so recover from error
|
||||
}
|
||||
setValue(nodep, valuep);
|
||||
}
|
||||
}
|
||||
void visit(AstVarXRef* nodep) override {
|
||||
|
|
@ -606,12 +605,14 @@ private:
|
|||
}
|
||||
void visit(AstConst* nodep) override {
|
||||
checkNodeInfo(nodep);
|
||||
if (!m_checkOnly && optimizable()) newValue(nodep, nodep);
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
newValue(nodep, nodep);
|
||||
}
|
||||
void visit(AstInitArray* nodep) override {
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!m_checkOnly && optimizable()) newValue(nodep, nodep);
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
newValue(nodep, nodep);
|
||||
}
|
||||
void visit(AstInitItem* nodep) override {
|
||||
checkNodeInfo(nodep);
|
||||
|
|
@ -620,62 +621,55 @@ private:
|
|||
void visit(AstEnumItemRef* nodep) override {
|
||||
checkNodeInfo(nodep);
|
||||
UASSERT_OBJ(nodep->itemp(), nodep, "Not linked");
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
AstNode* const valuep = nodep->itemp()->valuep();
|
||||
if (valuep) {
|
||||
iterateAndNextConstNull(valuep);
|
||||
if (!optimizable()) return;
|
||||
newValue(nodep, fetchValue(valuep));
|
||||
} else {
|
||||
clearOptimizable(nodep, "No value found for enum item"); // LCOV_EXCL_LINE
|
||||
}
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
AstNode* const valuep = nodep->itemp()->valuep();
|
||||
if (valuep) {
|
||||
iterateAndNextConstNull(valuep);
|
||||
if (!optimizable()) return;
|
||||
newValue(nodep, fetchValue(valuep));
|
||||
} else {
|
||||
clearOptimizable(nodep, "No value found for enum item"); // LCOV_EXCL_LINE
|
||||
}
|
||||
}
|
||||
void visit(AstNodeUniop* nodep) override {
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num());
|
||||
}
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num());
|
||||
}
|
||||
void visit(AstNodeBiop* nodep) override {
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
AstConst* const valuep = newConst(nodep);
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num());
|
||||
// See #5490. 'numberOperate' on partially out of range select yields 'x' bits,
|
||||
// but in reality it would yield '0's without V3Table, so force 'x' bits to '0',
|
||||
// to ensure the result is the same with and without V3Table.
|
||||
if (!m_params && VN_IS(nodep, Sel) && valuep->num().isAnyX()) {
|
||||
const V3Number num{valuep, valuep->width(), valuep->num()};
|
||||
valuep->num().opBitsOne(num);
|
||||
}
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
AstConst* const valuep = newConst(nodep);
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num());
|
||||
// See #5490. 'numberOperate' on partially out of range select yields 'x' bits,
|
||||
// but in reality it would yield '0's without V3Table, so force 'x' bits to '0',
|
||||
// to ensure the result is the same with and without V3Table.
|
||||
if (!m_params && VN_IS(nodep, Sel) && valuep->num().isAnyX()) {
|
||||
const V3Number num{valuep, valuep->width(), valuep->num()};
|
||||
valuep->num().opBitsOne(num);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeTriop* nodep) override {
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num(),
|
||||
fetchConst(nodep->thsp())->num());
|
||||
}
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num(), fetchConst(nodep->thsp())->num());
|
||||
}
|
||||
void visit(AstNodeQuadop* nodep) override {
|
||||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num(),
|
||||
fetchConst(nodep->thsp())->num(),
|
||||
fetchConst(nodep->fhsp())->num());
|
||||
}
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
nodep->numberOperate(newConst(nodep)->num(), fetchConst(nodep->lhsp())->num(),
|
||||
fetchConst(nodep->rhsp())->num(), fetchConst(nodep->thsp())->num(),
|
||||
fetchConst(nodep->fhsp())->num());
|
||||
}
|
||||
void visit(AstLogAnd* nodep) override {
|
||||
// Need to short circuit
|
||||
|
|
@ -773,62 +767,60 @@ private:
|
|||
clearOptimizable(nodep, "Array of non-basic dtype (e.g. array-of-array)");
|
||||
return;
|
||||
}
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
AstNode* const vscp = varOrScope(varrefp);
|
||||
AstInitArray* initp = nullptr;
|
||||
if (AstInitArray* const vscpnump = VN_CAST(fetchOutValueNull(vscp), InitArray)) {
|
||||
initp = vscpnump;
|
||||
} else if (AstInitArray* const vscpnump = VN_CAST(fetchValueNull(vscp), InitArray)) {
|
||||
initp = vscpnump;
|
||||
} else { // Assignment to unassigned variable, all bits are X
|
||||
// TODO generic initialization which builds X/arrays by recursion
|
||||
AstConst* const outconstp = new AstConst{
|
||||
nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
|
||||
if (basicp->isZeroInit()) {
|
||||
outconstp->num().setAllBits0();
|
||||
} else {
|
||||
outconstp->num().setAllBitsX();
|
||||
}
|
||||
|
||||
initp = new AstInitArray{nodep->fileline(), arrayp, outconstp};
|
||||
m_reclaimValuesp.push_back(initp);
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
AstNode* const vscp = varOrScope(varrefp);
|
||||
AstInitArray* initp = nullptr;
|
||||
if (AstInitArray* const vscpnump = VN_CAST(fetchOutValueNull(vscp), InitArray)) {
|
||||
initp = vscpnump;
|
||||
} else if (AstInitArray* const vscpnump = VN_CAST(fetchValueNull(vscp), InitArray)) {
|
||||
initp = vscpnump;
|
||||
} else { // Assignment to unassigned variable, all bits are X
|
||||
// TODO generic initialization which builds X/arrays by recursion
|
||||
AstConst* const outconstp
|
||||
= new AstConst{nodep->fileline(), AstConst::WidthedValue{}, basicp->widthMin(), 0};
|
||||
if (basicp->isZeroInit()) {
|
||||
outconstp->num().setAllBits0();
|
||||
} else {
|
||||
outconstp->num().setAllBitsX();
|
||||
}
|
||||
const uint32_t index = fetchConst(selp->bitp())->toUInt();
|
||||
AstNodeExpr* const valuep = newTrackedClone(fetchValue(valueFromp));
|
||||
UINFO(9, " set val[" << index << "] = " << valuep);
|
||||
// Values are in the "real" tree under the InitArray so can eventually extract it,
|
||||
// Not in the usual setValue (via m_varAux)
|
||||
initp->addIndexValuep(index, valuep);
|
||||
UINFOTREE(9, initp, "", "array");
|
||||
assignOutValue(nodep, vscp, initp);
|
||||
|
||||
initp = new AstInitArray{nodep->fileline(), arrayp, outconstp};
|
||||
m_reclaimValuesp.push_back(initp);
|
||||
}
|
||||
const uint32_t index = fetchConst(selp->bitp())->toUInt();
|
||||
AstNodeExpr* const valuep = newTrackedClone(fetchValue(valueFromp));
|
||||
UINFO(9, " set val[" << index << "] = " << valuep);
|
||||
// Values are in the "real" tree under the InitArray so can eventually extract it,
|
||||
// Not in the usual setValue (via m_varAux)
|
||||
initp->addIndexValuep(index, valuep);
|
||||
UINFOTREE(9, initp, "", "array");
|
||||
assignOutValue(nodep, vscp, initp);
|
||||
}
|
||||
void handleAssignSel(AstNodeAssign* nodep, AstSel* selp, AstNodeExpr* valueFromp) {
|
||||
AstVarRef* varrefp = nullptr;
|
||||
V3Number lsb{nodep};
|
||||
handleAssignSelRecurse(nodep, selp, varrefp /*ref*/, lsb /*ref*/, 0);
|
||||
if (!m_checkOnly && optimizable()) {
|
||||
UASSERT_OBJ(varrefp, nodep,
|
||||
"Indicated optimizable, but no variable found on RHS of select");
|
||||
AstNode* const vscp = varOrScope(varrefp);
|
||||
AstConst* outconstp = nullptr;
|
||||
if (AstConst* const vscpnump = fetchOutConstNull(vscp)) {
|
||||
outconstp = vscpnump;
|
||||
} else if (AstConst* const vscpnump = fetchConstNull(vscp)) {
|
||||
outconstp = vscpnump;
|
||||
} else { // Assignment to unassigned variable, all bits are X or 0
|
||||
outconstp = new AstConst{nodep->fileline(), AstConst::WidthedValue{},
|
||||
varrefp->varp()->widthMin(), 0};
|
||||
if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) {
|
||||
outconstp->num().setAllBits0();
|
||||
} else {
|
||||
outconstp->num().setAllBitsX();
|
||||
}
|
||||
m_reclaimValuesp.emplace_back(outconstp);
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
UASSERT_OBJ(varrefp, nodep,
|
||||
"Indicated optimizable, but no variable found on RHS of select");
|
||||
AstNode* const vscp = varOrScope(varrefp);
|
||||
AstConst* outconstp = nullptr;
|
||||
if (AstConst* const vscpnump = fetchOutConstNull(vscp)) {
|
||||
outconstp = vscpnump;
|
||||
} else if (AstConst* const vscpnump = fetchConstNull(vscp)) {
|
||||
outconstp = vscpnump;
|
||||
} else { // Assignment to unassigned variable, all bits are X or 0
|
||||
outconstp = new AstConst{nodep->fileline(), AstConst::WidthedValue{},
|
||||
varrefp->varp()->widthMin(), 0};
|
||||
if (varrefp->varp()->basicp() && varrefp->varp()->basicp()->isZeroInit()) {
|
||||
outconstp->num().setAllBits0();
|
||||
} else {
|
||||
outconstp->num().setAllBitsX();
|
||||
}
|
||||
outconstp->num().opSelInto(fetchConst(valueFromp)->num(), lsb, selp->widthConst());
|
||||
assignOutValue(nodep, vscp, outconstp);
|
||||
m_reclaimValuesp.emplace_back(outconstp);
|
||||
}
|
||||
outconstp->num().opSelInto(fetchConst(valueFromp)->num(), lsb, selp->widthConst());
|
||||
assignOutValue(nodep, vscp, outconstp);
|
||||
}
|
||||
void handleAssignSelRecurse(AstNodeAssign* nodep, AstSel* selp, AstVarRef*& outVarrefpRef,
|
||||
V3Number& lsbRef, int depth) {
|
||||
|
|
@ -1059,8 +1051,8 @@ private:
|
|||
iterateAndNextConstNull(nodep->stmtsp());
|
||||
if (!optimizable()) return;
|
||||
iterateAndNextConstNull(nodep->resultp());
|
||||
if (!optimizable()) return;
|
||||
if (!m_checkOnly) newValue(nodep, fetchValue(nodep->resultp()));
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
newValue(nodep, fetchValue(nodep->resultp()));
|
||||
}
|
||||
|
||||
void visit(AstJumpBlock* nodep) override {
|
||||
|
|
@ -1121,7 +1113,8 @@ private:
|
|||
if (jumpingOver()) return;
|
||||
checkNodeInfo(nodep);
|
||||
iterateConst(nodep->condp());
|
||||
if (!m_checkOnly && optimizable() && fetchConst(nodep->condp())->num().isEqZero()) {
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
if (fetchConst(nodep->condp())->num().isEqZero()) {
|
||||
UINFO(5, " LOOP TEST GO " << nodep);
|
||||
UASSERT_OBJ(!m_jumptargetp, nodep, "Jump inside jump");
|
||||
m_jumptargetp = nodep->loopp();
|
||||
|
|
@ -1274,8 +1267,7 @@ private:
|
|||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (m_checkOnly) return;
|
||||
if (!optimizable()) return; // Accelerate
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
|
||||
AstNode* nextArgp = nodep->exprsp();
|
||||
string result;
|
||||
|
|
@ -1357,8 +1349,7 @@ private:
|
|||
if (!optimizable()) return; // Accelerate
|
||||
checkNodeInfo(nodep);
|
||||
iterateChildrenConst(nodep);
|
||||
if (!optimizable()) return;
|
||||
if (m_checkOnly) return;
|
||||
if (m_checkOnly || !optimizable()) return;
|
||||
const std::string result = toStringRecurse(nodep->lhsp());
|
||||
if (!optimizable()) return;
|
||||
AstConst* const resultConstp = new AstConst{nodep->fileline(), AstConst::String{}, result};
|
||||
|
|
|
|||
|
|
@ -750,7 +750,7 @@ class TimingControlVisitor final : public VNVisitor {
|
|||
const AstCMethodHard* const methodp = VN_CAST(stmtExprp->exprp(), CMethodHard);
|
||||
if (!methodp || methodp->name() != "push_back") return false;
|
||||
const AstVarRef* const queueRefp = VN_CAST(methodp->fromp(), VarRef);
|
||||
return queueRefp && queueRefp->name().rfind("__VprocessQueue_", 0) == 0;
|
||||
return queueRefp && queueRefp->varp()->processQueue();
|
||||
}
|
||||
// Register a callback so killing a process-backed fork branch decrements the join counter
|
||||
void addForkOnKill(AstBegin* const beginp, AstVarScope* const forkVscp) const {
|
||||
|
|
|
|||
|
|
@ -1652,7 +1652,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
if (nodep->seedp()) iterateCheckSigned32(nodep, "seed", nodep->seedp(), BOTH);
|
||||
}
|
||||
}
|
||||
void visit(AstSExprGotoRep* nodep) override {
|
||||
void visit(AstSGotoRep* nodep) override {
|
||||
assertAtExpr(nodep);
|
||||
if (m_vup->prelim()) {
|
||||
iterateCheckBool(nodep, "exprp", nodep->exprp(), BOTH);
|
||||
|
|
@ -1660,6 +1660,19 @@ class WidthVisitor final : public VNVisitor {
|
|||
nodep->dtypeSetBit();
|
||||
}
|
||||
}
|
||||
void visit(AstSThroughout* nodep) override {
|
||||
m_hasSExpr = true;
|
||||
assertAtExpr(nodep);
|
||||
if (m_vup->prelim()) {
|
||||
// lhsp is a boolean expression, not a sequence -- clear m_underSExpr temporarily
|
||||
VL_RESTORER(m_underSExpr);
|
||||
m_underSExpr = false;
|
||||
iterateCheckBool(nodep, "lhsp", nodep->lhsp(), BOTH);
|
||||
m_underSExpr = true;
|
||||
iterate(nodep->rhsp());
|
||||
nodep->dtypeSetBit();
|
||||
}
|
||||
}
|
||||
void visit(AstSExpr* nodep) override {
|
||||
VL_RESTORER(m_underSExpr);
|
||||
m_underSExpr = true;
|
||||
|
|
@ -3301,7 +3314,7 @@ class WidthVisitor final : public VNVisitor {
|
|||
for (AstNode *nextip, *itemp = nodep->itemsp(); itemp; itemp = nextip) {
|
||||
nextip = itemp->nextp(); // iterate may cause the node to get replaced
|
||||
// InsideRange will get replaced with Lte&Gte and finalized later
|
||||
if (!VN_IS(itemp, InsideRange))
|
||||
if (!VN_IS(itemp, InsideRange) && !itemp->dtypep()->isNonPackedArray())
|
||||
iterateCheck(nodep, "Inside Item", itemp, CONTEXT_DET, FINAL, expDTypep,
|
||||
EXTEND_EXP);
|
||||
}
|
||||
|
|
@ -3589,6 +3602,32 @@ class WidthVisitor final : public VNVisitor {
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
return;
|
||||
}
|
||||
if (AstCell* const cellp = VN_CAST(foundp, Cell)) {
|
||||
// Sub-interface cell selection (e.g. vif.tx): resolve to the
|
||||
// companion __Viftop var created by V3LinkCells for its dtype.
|
||||
if (VN_IS(cellp->modp(), Iface)) {
|
||||
const string viftopName = cellp->name() + "__Viftop";
|
||||
AstNodeModule* const parentIfacep = adtypep->ifaceViaCellp();
|
||||
AstVar* viftopVarp = nullptr;
|
||||
for (AstNode* itemp = parentIfacep->stmtsp(); itemp;
|
||||
itemp = itemp->nextp()) {
|
||||
if (AstVar* const vp = VN_CAST(itemp, Var)) {
|
||||
if (vp->name() == viftopName) {
|
||||
viftopVarp = vp;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
UASSERT_OBJ(viftopVarp, nodep,
|
||||
"No __Viftop variable for sub-interface cell");
|
||||
if (!viftopVarp->didWidth()) userIterate(viftopVarp, nullptr);
|
||||
nodep->dtypep(viftopVarp->dtypep());
|
||||
nodep->varp(viftopVarp);
|
||||
viftopVarp->sensIfacep(VN_AS(cellp->modp(), Iface));
|
||||
nodep->didWidth(true);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UINFO(1, "found object " << foundp);
|
||||
nodep->v3fatalSrc("MemberSel of non-variable\n"
|
||||
<< nodep->warnContextPrimary() << '\n'
|
||||
|
|
@ -3710,12 +3749,14 @@ class WidthVisitor final : public VNVisitor {
|
|||
AstNode* memberSelIface(AstMemberSel* nodep, AstIfaceRefDType* adtypep) {
|
||||
// Returns node if ok
|
||||
// No need to width-resolve the interface, as it was done when we did the child
|
||||
AstNodeModule* const ifacep = adtypep->ifacep();
|
||||
// ifaceViaCellp() handles dtypes with cellp-only (no ifacep), as produced
|
||||
// by sub-interface selection, enabling chained access (e.g. vif.tx.Tx).
|
||||
AstNodeModule* const ifacep = adtypep->ifaceViaCellp();
|
||||
UASSERT_OBJ(ifacep, nodep, "Unlinked");
|
||||
VSpellCheck speller;
|
||||
for (AstNode* itemp = ifacep->stmtsp(); itemp; itemp = itemp->nextp()) {
|
||||
if (itemp->name() == nodep->name()) return itemp;
|
||||
if (VN_IS(itemp, Var) || VN_IS(itemp, Modport)) {
|
||||
if (VN_IS(itemp, Var) || VN_IS(itemp, Modport) || VN_IS(itemp, Cell)) {
|
||||
speller.pushCandidate(itemp->prettyName());
|
||||
}
|
||||
}
|
||||
|
|
@ -6030,12 +6071,8 @@ class WidthVisitor final : public VNVisitor {
|
|||
const AstNodeDType* const rhsDtp = nodep->rhsp()->dtypep()->skipRefp();
|
||||
// Only check if number of states match for unpacked array to unpacked array
|
||||
// assignments
|
||||
const bool lhsIsUnpackArray
|
||||
= VN_IS(lhsDtp, UnpackArrayDType) || VN_IS(lhsDtp, DynArrayDType)
|
||||
|| VN_IS(lhsDtp, QueueDType) || VN_IS(lhsDtp, AssocArrayDType);
|
||||
const bool rhsIsUnpackArray
|
||||
= VN_IS(rhsDtp, UnpackArrayDType) || VN_IS(rhsDtp, DynArrayDType)
|
||||
|| VN_IS(rhsDtp, QueueDType) || VN_IS(rhsDtp, AssocArrayDType);
|
||||
const bool lhsIsUnpackArray = lhsDtp->isNonPackedArray();
|
||||
const bool rhsIsUnpackArray = rhsDtp->isNonPackedArray();
|
||||
if (lhsIsUnpackArray && rhsIsUnpackArray) {
|
||||
if (lhsDtp->isFourstate() != rhsDtp->isFourstate()) {
|
||||
nodep->v3error("Assignment between 2-state and 4-state types requires "
|
||||
|
|
|
|||
|
|
@ -76,6 +76,7 @@
|
|||
#include "V3LinkLevel.h"
|
||||
#include "V3LinkParse.h"
|
||||
#include "V3LinkResolve.h"
|
||||
#include "V3LinkWith.h"
|
||||
#include "V3Localize.h"
|
||||
#include "V3MergeCond.h"
|
||||
#include "V3Name.h"
|
||||
|
|
@ -182,6 +183,11 @@ static void process() {
|
|||
|
||||
V3LinkDot::linkDotParamed(v3Global.rootp()); // Cleanup as made new modules
|
||||
V3LinkLValue::linkLValue(v3Global.rootp()); // Resolve new VarRefs
|
||||
|
||||
// Link cleanup of 'with' as final link phase before V3Width
|
||||
// (called only once, not when width a single params)
|
||||
V3LinkWith::linkWith(v3Global.rootp());
|
||||
|
||||
V3Error::abortIfErrors();
|
||||
|
||||
// Fix any remaining cross-interface refs created during V3Width::widthParamsEdit
|
||||
|
|
|
|||
|
|
@ -6829,7 +6829,7 @@ sexpr<nodeExprp>: // ==IEEE: sequence_expr (The name sexpr is important as reg
|
|||
new AstConst{$<fl>2, 0u}, nullptr, true}; }
|
||||
// // IEEE: goto_repetition (single count form)
|
||||
| ~p~sexpr/*sexpression_or_dist*/ yP_BRAMINUSGT constExpr ']'
|
||||
{ $$ = new AstSExprGotoRep{$<fl>2, $1, $3}; }
|
||||
{ $$ = new AstSGotoRep{$<fl>2, $1, $3}; }
|
||||
// // IEEE: goto_repetition (range form -- unsupported)
|
||||
| ~p~sexpr/*sexpression_or_dist*/ yP_BRAMINUSGT constExpr ':' constExpr ']'
|
||||
{ $$ = $1; BBUNSUP($<fl>2, "Unsupported: [-> range goto repetition"); DEL($3); DEL($5); }
|
||||
|
|
@ -6867,7 +6867,7 @@ sexpr<nodeExprp>: // ==IEEE: sequence_expr (The name sexpr is important as reg
|
|||
| yFIRST_MATCH '(' sexpr ',' sequence_match_itemList ')'
|
||||
{ $$ = $3; BBUNSUP($1, "Unsupported: first_match (in sequence expression)"); DEL($5); }
|
||||
| ~p~sexpr/*sexpression_or_dist*/ yTHROUGHOUT sexpr
|
||||
{ $$ = $1; BBUNSUP($2, "Unsupported: throughout (in sequence expression)"); DEL($3); }
|
||||
{ $$ = new AstSThroughout{$2, $1, $3}; }
|
||||
// // Below pexpr's are really sequence_expr, but avoid conflict
|
||||
// // IEEE: sexpr yWITHIN sexpr
|
||||
| ~p~sexpr yWITHIN sexpr
|
||||
|
|
@ -6896,11 +6896,11 @@ cycle_delay_range<delayp>: // IEEE: ==cycle_delay_range
|
|||
{ $$ = new AstDelay{$1, $3, true};
|
||||
$$->rhsp($5); }
|
||||
| yP_POUNDPOUND yP_BRASTAR ']'
|
||||
{ $$ = new AstDelay{$1, new AstConst{$1, AstConst::BitFalse{}}, true};
|
||||
BBUNSUP($<fl>1, "Unsupported: ## [*] cycle delay range expression"); }
|
||||
{ $$ = new AstDelay{$1, new AstConst{$1, 0}, true};
|
||||
$$->rhsp(new AstUnbounded{$1}); }
|
||||
| yP_POUNDPOUND yP_BRAPLUSKET
|
||||
{ $$ = new AstDelay{$1, new AstConst{$1, AstConst::BitFalse{}}, true};
|
||||
BBUNSUP($<fl>1, "Unsupported: ## [+] cycle delay range expression"); }
|
||||
{ $$ = new AstDelay{$1, new AstConst{$1, 1}, true};
|
||||
$$->rhsp(new AstUnbounded{$1}); }
|
||||
;
|
||||
|
||||
sequence_match_itemList<nodep>: // IEEE: [sequence_match_item] part of sequence_expr
|
||||
|
|
@ -6925,7 +6925,7 @@ boolean_abbrev<nodeExprp>: // ==IEEE: boolean_abbrev
|
|||
| yP_BRAEQ constExpr ':' constExpr ']'
|
||||
{ $$ = $2; BBUNSUP($<fl>1, "Unsupported: [= boolean abbrev expression"); DEL($4); }
|
||||
// // IEEE: goto_repetition
|
||||
// // Goto repetition [->N] handled in sexpr rule (AstSExprGotoRep)
|
||||
// // Goto repetition [->N] handled in sexpr rule (AstSGotoRep)
|
||||
// // Range form [->M:N] also handled there (unsupported)
|
||||
;
|
||||
|
||||
|
|
|
|||
|
|
@ -8,16 +8,20 @@
|
|||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
package p;
|
||||
class W #(type T = int);
|
||||
class W #(
|
||||
type T = int
|
||||
);
|
||||
T v;
|
||||
endclass
|
||||
|
||||
class Holder #(type U = W#());
|
||||
class Holder #(
|
||||
type U = W#()
|
||||
);
|
||||
U u;
|
||||
endclass
|
||||
|
||||
typedef Holder#() H_imp_t; // implicit default
|
||||
typedef Holder#(W#(int)) H_exp_t; // explicit equivalent default
|
||||
typedef Holder#() H_imp_t; // implicit default
|
||||
typedef Holder#(W#(int)) H_exp_t; // explicit equivalent default
|
||||
endpackage
|
||||
|
||||
module t;
|
||||
|
|
@ -29,8 +33,8 @@ module t;
|
|||
// verilator lint_off CASTCONST
|
||||
// verilator lint_off WIDTHTRUNC
|
||||
if (!$cast(exp, imp)) begin
|
||||
// verilator lint_on WIDTHTRUNC
|
||||
// verilator lint_on CASTCONST
|
||||
// verilator lint_on WIDTHTRUNC
|
||||
// verilator lint_on CASTCONST
|
||||
$display("WRONG_TYPE");
|
||||
$fatal;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
%Error: t/t_constraint_before_randc_bad.v:11:42: Randc variables not allowed in 'solve before' (IEEE 1800-2023 18.5.9)
|
||||
: ... note: In instance 't'
|
||||
11 | constraint raint2_bad {solve b1 before b2;}
|
||||
| ^~
|
||||
t/t_constraint_before_randc_bad.v:11:26: ... Location of restricting expression
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
%Error: t/t_constraint_dist_randc_bad.v:10:22: Randc variables not allowed in 'constraint dist' (IEEE 1800-2023 18.5.3)
|
||||
: ... note: In instance 't'
|
||||
10 | constraint c_bad { rc dist {3 := 0, 10 := 5}; }
|
||||
| ^~
|
||||
t/t_constraint_dist_randc_bad.v:10:25: ... Location of restricting expression
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
%Error: t/t_constraint_soft_randc_bad.v:10:27: Randc variables not allowed in 'constraint soft' (IEEE 1800-2023 18.5.13.1)
|
||||
: ... note: In instance 't'
|
||||
10 | constraint c_bad { soft rc > 4; }
|
||||
| ^~
|
||||
t/t_constraint_soft_randc_bad.v:10:22: ... Location of restricting expression
|
||||
|
|
|
|||
|
|
@ -0,0 +1,25 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
if not test.have_solver:
|
||||
test.skip("No constraint solver installed")
|
||||
|
||||
test.compile()
|
||||
|
||||
solver_log = test.obj_dir + "/solver.log"
|
||||
|
||||
test.execute(all_run_flags=['+verilator+solver+file+' + solver_log])
|
||||
|
||||
test.file_grep(solver_log, '# Verilator solver log')
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
class Packet;
|
||||
rand int one;
|
||||
constraint a {one > 0 && one < 2;}
|
||||
endclass
|
||||
|
||||
module t;
|
||||
|
||||
Packet p;
|
||||
|
||||
int v;
|
||||
|
||||
initial begin
|
||||
p = new;
|
||||
v = p.randomize();
|
||||
if (v != 1) $stop;
|
||||
if (p.one != 1) $stop;
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -11,13 +11,14 @@
|
|||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
module t(/*AUTOARG*/);
|
||||
module t ( /*AUTOARG*/);
|
||||
generate
|
||||
if (1) begin : defs
|
||||
function automatic logic foo;
|
||||
foo = 1'b1;
|
||||
endfunction
|
||||
end else begin : defs
|
||||
end
|
||||
else begin : defs
|
||||
function automatic logic foo;
|
||||
foo = 1'b0;
|
||||
endfunction
|
||||
|
|
|
|||
|
|
@ -4,14 +4,43 @@
|
|||
// SPDX-FileCopyrightText: 2024 Antmicro
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checks(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got='%s' exp='%s'\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
|
||||
function string validate_time_precision(string precision);
|
||||
static string valid_precision[$] = '{"ps", "ns", "us", "ms", "s"};
|
||||
if (!(precision inside {valid_precision})) begin
|
||||
return "none";
|
||||
end
|
||||
return precision;
|
||||
endfunction
|
||||
|
||||
initial begin
|
||||
automatic int q[$] = {1, 2};
|
||||
string s;
|
||||
|
||||
if (!(1 inside {q[0], q[1]})) $stop;
|
||||
if (3 inside {q[0], q[1]}) $stop;
|
||||
|
||||
s = validate_time_precision("ps");
|
||||
`checks(s, "ps");
|
||||
s = validate_time_precision("ns");
|
||||
`checks(s, "ns");
|
||||
s = validate_time_precision("us");
|
||||
`checks(s, "us");
|
||||
s = validate_time_precision("ms");
|
||||
`checks(s, "ms");
|
||||
s = validate_time_precision("s");
|
||||
`checks(s, "s");
|
||||
s = validate_time_precision("random");
|
||||
`checks(s, "none");
|
||||
s = validate_time_precision("");
|
||||
`checks(s, "none");
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
|
|
|
|||
|
|
@ -6,7 +6,7 @@
|
|||
|
||||
interface my_if;
|
||||
logic clk = 0;
|
||||
bit clk_active = 0;
|
||||
bit clk_active = 0;
|
||||
|
||||
initial begin
|
||||
wait (clk_active);
|
||||
|
|
@ -27,8 +27,8 @@ class Driver;
|
|||
endclass
|
||||
|
||||
module t;
|
||||
my_if intf();
|
||||
my_if intf_unused(); // Second instance triggered the bug
|
||||
my_if intf ();
|
||||
my_if intf_unused (); // Second instance triggered the bug
|
||||
|
||||
initial begin
|
||||
automatic Driver d = new;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile()
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
// Issue #7203: virtual interface select from sub-interface instance.
|
||||
// The original reproducer: vip_agent holds vip_vif; vip_driver selects
|
||||
// agent.vif.tx (a vip_tx_if sub-interface) into tx_vif.
|
||||
|
||||
interface vip_tx_if (
|
||||
output reg Tx
|
||||
);
|
||||
endinterface
|
||||
|
||||
interface vip_if (
|
||||
output reg Tx
|
||||
);
|
||||
vip_tx_if tx (Tx);
|
||||
endinterface
|
||||
|
||||
package vip_pkg;
|
||||
typedef virtual vip_if vip_vif;
|
||||
typedef virtual vip_tx_if vip_tx_vif;
|
||||
|
||||
class vip_agent;
|
||||
vip_vif vif;
|
||||
endclass
|
||||
|
||||
class vip_driver;
|
||||
vip_vif vif;
|
||||
vip_tx_vif tx_vif;
|
||||
virtual function void build_phase(vip_agent agent);
|
||||
// Sub-interface select: dtype(agent.vif) -> vip_vif -> vip_if
|
||||
vif = agent.vif;
|
||||
tx_vif = agent.vif.tx;
|
||||
endfunction
|
||||
// Chained member access through sub-interface
|
||||
virtual function void drive(logic val);
|
||||
vif.tx.Tx = val;
|
||||
endfunction
|
||||
endclass
|
||||
endpackage
|
||||
|
||||
module t;
|
||||
logic wire_Tx;
|
||||
vip_if vif_inst (.Tx(wire_Tx));
|
||||
|
||||
initial begin
|
||||
automatic vip_pkg::vip_agent agent = new;
|
||||
automatic vip_pkg::vip_driver driver = new;
|
||||
|
||||
agent.vif = vif_inst;
|
||||
driver.vif = vif_inst;
|
||||
|
||||
// Test 1 (issue reproducer): sub-interface select compiles and runs
|
||||
driver.build_phase(agent);
|
||||
|
||||
// Test 2: tx_vif now points to the sub-interface; write through it
|
||||
driver.tx_vif.Tx = 1'b1;
|
||||
`checkd(wire_Tx, 1'b1)
|
||||
|
||||
// Test 3: chained member write through virtual interface
|
||||
driver.drive(1'b0);
|
||||
`checkd(wire_Tx, 1'b0)
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(verilator_flags2=["--binary"])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,116 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
// Original #6281 reproducer: parameter passed via localparam variable
|
||||
// vs. literal constant should resolve to the same specialization.
|
||||
// Fixed by ParameterizedHierBlocks::areSame fallback (landed earlier).
|
||||
class ClsIntDefault #(
|
||||
parameter int P = 32
|
||||
);
|
||||
function int get_p;
|
||||
return P;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Parameter with byte cast default value
|
||||
class ClsByteCast #(
|
||||
parameter byte P = byte'(8)
|
||||
);
|
||||
function byte get_p;
|
||||
return P;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Parameter with int cast default value
|
||||
class ClsIntCast #(
|
||||
parameter int P = int'(42)
|
||||
);
|
||||
function int get_p;
|
||||
return P;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Parameter with signed cast default value
|
||||
class ClsSignedCast #(
|
||||
parameter int P = int'(-5)
|
||||
);
|
||||
function int get_p;
|
||||
return P;
|
||||
endfunction
|
||||
endclass
|
||||
|
||||
// Module with cast default (cell array test)
|
||||
module sub #(
|
||||
parameter byte P = byte'(8)
|
||||
);
|
||||
initial begin
|
||||
`checkd(P, 8);
|
||||
end
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
// Original #6281 case: localparam variable vs. literal constant
|
||||
localparam int WIDTH = 32;
|
||||
ClsIntDefault #(32) orig_a;
|
||||
ClsIntDefault #(WIDTH) orig_b;
|
||||
|
||||
// Byte cast default: #() and #(8) should be same type
|
||||
ClsByteCast #() byte_a;
|
||||
ClsByteCast #(8) byte_b;
|
||||
|
||||
// Int cast default: #() and #(42) should be same type
|
||||
ClsIntCast #() int_a;
|
||||
ClsIntCast #(42) int_b;
|
||||
|
||||
// Signed cast default: #() and #(-5) should be same type
|
||||
ClsSignedCast #() signed_a;
|
||||
ClsSignedCast #(-5) signed_b;
|
||||
|
||||
// Multiple instances (template mutation safety)
|
||||
ClsByteCast #() multi_a;
|
||||
ClsByteCast #(8) multi_b;
|
||||
ClsByteCast #() multi_c;
|
||||
ClsByteCast #(8) multi_d;
|
||||
|
||||
// Module with cast default
|
||||
sub #() sub_default ();
|
||||
sub #(8) sub_explicit ();
|
||||
|
||||
initial begin
|
||||
orig_a = new;
|
||||
orig_b = orig_a;
|
||||
`checkd(orig_b.get_p(), 32);
|
||||
|
||||
byte_a = new;
|
||||
byte_b = byte_a;
|
||||
`checkd(byte_a.get_p(), 8);
|
||||
`checkd(byte_b.get_p(), 8);
|
||||
|
||||
int_a = new;
|
||||
int_b = int_a;
|
||||
`checkd(int_a.get_p(), 42);
|
||||
`checkd(int_b.get_p(), 42);
|
||||
|
||||
signed_a = new;
|
||||
signed_b = signed_a;
|
||||
`checkd(signed_a.get_p(), -5);
|
||||
`checkd(signed_b.get_p(), -5);
|
||||
|
||||
multi_a = new;
|
||||
multi_b = multi_a;
|
||||
multi_c = multi_a;
|
||||
multi_d = multi_a;
|
||||
`checkd(multi_d.get_p(), 8);
|
||||
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
%Warning-WIDTHTRUNC: t/t_param_width_loc_bad.v:19:21: Operator VAR 'PARAM' expects 1 bits on the Initial value, but Initial value's CONST '32'h0' generates 32 bits.
|
||||
%Warning-WIDTHTRUNC: t/t_param_width_loc_bad.v:19:21: Operator VAR 'PARAM' expects 1 bits on the Initial value, but Initial value's CONST '32'h1' generates 32 bits.
|
||||
: ... note: In instance 't.test_i'
|
||||
19 | parameter logic PARAM = 1'b0
|
||||
| ^~~~~
|
||||
|
|
|
|||
|
|
@ -7,7 +7,7 @@
|
|||
module t;
|
||||
|
||||
// bug1624
|
||||
test #(.PARAM(32'd0)) test_i ();
|
||||
test #(.PARAM(32'd1)) test_i ();
|
||||
|
||||
initial begin
|
||||
$write("*-* All Finished *-*\n");
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ module t (
|
|||
wire a = crc[0];
|
||||
wire b = crc[1];
|
||||
wire c = crc[2];
|
||||
wire d = crc[3];
|
||||
wire e = crc[4];
|
||||
|
||||
wire [63:0] result = {61'h0, c, b, a};
|
||||
|
||||
|
|
@ -46,44 +48,70 @@ module t (
|
|||
end
|
||||
end
|
||||
|
||||
// Basic ##[1:3] range delay (CRC-driven, always-true consequent)
|
||||
// Basic ##[1:3] range delay
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[1:3] 1'b1);
|
||||
a |-> ##[1:3] (a | b | c | d | e));
|
||||
|
||||
// ##[2:4] range delay
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
b |-> ##[2:4] 1'b1);
|
||||
b |-> ##[2:4] (a | b | c | d | e));
|
||||
|
||||
// Degenerate ##[2:2] (equivalent to ##2)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[2:2] 1'b1);
|
||||
a |-> ##[2:2] (a | b | c | d | e));
|
||||
|
||||
// Multi-step: ##[1:2] then ##1 (both consequents always true)
|
||||
// Multi-step: ##[1:2] then ##1
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[1:2] 1'b1 ##1 1'b1);
|
||||
a |-> ##[1:2] (a | b | c | d | e) ##1 (a | b | c | d | e));
|
||||
|
||||
// Large range ##[1:10000] (scalability, O(1) code size)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[1:10000] 1'b1);
|
||||
a |-> ##[1:10000] (a | b | c | d | e));
|
||||
|
||||
// Range with binary SExpr: nextStep has delay > 0 after range match
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> b ##[1:2] 1'b1 ##3 1'b1);
|
||||
a |-> b ##[1:2] (a | b | c | d | e) ##3 (a | b | c | d | e));
|
||||
|
||||
// Binary SExpr without implication (covers firstStep.exprp path without antecedent)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a ##[1:3] 1'b1);
|
||||
a ##[1:3] (a | b | c | d | e));
|
||||
|
||||
// Implication with binary SExpr RHS (covers antExprp AND firstStep.exprp)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> b ##[1:2] 1'b1);
|
||||
a |-> b ##[1:2] (a | b | c | d | e));
|
||||
|
||||
// Fixed delay before range (covers firstStep.delay path in IDLE)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##2 1'b1 ##[1:3] 1'b1);
|
||||
a |-> ##2 (a | b | c | d | e) ##[1:3] (a | b | c | d | e));
|
||||
|
||||
// Unary range with no antecedent and no preExpr (covers unconditional start)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
##[1:3] 1'b1);
|
||||
##[1:3] (a | b | c | d | e));
|
||||
|
||||
// ##[+] (= ##[1:$]): wait >= 1 cycle for b (CRC-driven, exercises CHECK stay)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[+] b);
|
||||
|
||||
// ##[*] (= ##[0:$]): check b immediately or after >= 1 cycle
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[*] b);
|
||||
|
||||
// ##[2:$]: explicit min > 1, wait then check c (exercises WAIT_MIN)
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
b |-> ##[2:$] c);
|
||||
|
||||
// ##[1:$]: explicit form equivalent to ##[+]
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[1:$] c);
|
||||
|
||||
// Unary ##[+] and ##[*] without antecedent
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
##[+] b);
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
##[*] b);
|
||||
|
||||
// Multi-step with unbounded range: ##[+] then fixed ##1
|
||||
assert property (@(posedge clk) disable iff (cyc < 2)
|
||||
a |-> ##[+] b ##1 (a | b | c | d | e));
|
||||
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -11,9 +11,17 @@
|
|||
: ... note: In instance 't'
|
||||
24 | a3: assert property (@(posedge clk) a |-> ##[5:2] b);
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_property_sexpr_range_delay_bad.v:27:45: Unsupported: ##0 in range delays
|
||||
%Error-UNSUPPORTED: t/t_property_sexpr_range_delay_bad.v:27:45: Unsupported: ##0 in bounded range delays
|
||||
: ... note: In instance 't'
|
||||
27 | a4: assert property (@(posedge clk) a |-> ##[0:3] b);
|
||||
| ^~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error: t/t_property_sexpr_range_delay_bad.v:30:45: Range delay minimum must be an elaboration-time constant (IEEE 1800-2023 16.7)
|
||||
: ... note: In instance 't'
|
||||
30 | a5: assert property (@(posedge clk) a |-> ##[cyc:$] b);
|
||||
| ^~
|
||||
%Error: t/t_property_sexpr_range_delay_bad.v:34:45: Range delay bounds must be non-negative (IEEE 1800-2023 16.7)
|
||||
: ... note: In instance 't'
|
||||
34 | a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
||||
| ^~
|
||||
%Error: Exiting due to
|
||||
|
|
|
|||
|
|
@ -26,4 +26,11 @@ module t;
|
|||
// ##0 in range
|
||||
a4: assert property (@(posedge clk) a |-> ##[0:3] b);
|
||||
|
||||
// Non-constant minimum in unbounded range
|
||||
a5: assert property (@(posedge clk) a |-> ##[cyc:$] b);
|
||||
|
||||
// Negative minimum in unbounded range
|
||||
localparam int NEG = -1;
|
||||
a6: assert property (@(posedge clk) a |-> ##[NEG:$] b);
|
||||
|
||||
endmodule
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
%Error-UNSUPPORTED: t/t_randomize_nested_unsup.v:17:41: Unsupported: randomize() nested in inline randomize() constraints
|
||||
: ... note: In instance 't'
|
||||
17 | if (a.randomize() with {rdata == aa.randomize();} == 0) $stop;
|
||||
| ^~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
|
|
|
|||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('simulator')
|
||||
|
||||
test.compile(timing_loop=True, verilator_flags2=['--assert', '--timing'])
|
||||
|
||||
test.execute()
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,67 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// verilog_format: off
|
||||
`define stop $stop
|
||||
`define checkh(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
`define checkd(gotv, expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
|
||||
// verilog_format: on
|
||||
|
||||
// IEEE 1800-2023 16.9.9: expr throughout seq
|
||||
// CRC-driven random stimulus exercises throughout with varying cond/a/b signals.
|
||||
|
||||
module t (
|
||||
input clk
|
||||
);
|
||||
integer cyc = 0;
|
||||
reg [63:0] crc = '0;
|
||||
|
||||
// Derive signals from non-adjacent CRC bits (gap > max delay to avoid LFSR correlation)
|
||||
wire cond = crc[0];
|
||||
wire a = crc[4];
|
||||
wire b = crc[8];
|
||||
|
||||
int count_fail1 = 0;
|
||||
int count_fail2 = 0;
|
||||
int count_fail3 = 0;
|
||||
|
||||
// Test 1: a |-> (cond throughout (1'b1 ##3 1'b1))
|
||||
// If a fires, cond must hold for 4 consecutive ticks (start + 3 delay ticks).
|
||||
assert property (@(posedge clk) disable iff (cyc < 10)
|
||||
a |-> (cond throughout (1'b1 ##3 1'b1)))
|
||||
else count_fail1 <= count_fail1 + 1;
|
||||
|
||||
// Test 2: a |-> (cond throughout (1'b1 ##1 b))
|
||||
// If a fires, cond must hold for 2 ticks and b must be true at tick +1.
|
||||
assert property (@(posedge clk) disable iff (cyc < 10)
|
||||
a |-> (cond throughout (1'b1 ##1 b)))
|
||||
else count_fail2 <= count_fail2 + 1;
|
||||
|
||||
// Test 3: a |-> (cond throughout b)
|
||||
// No delay: degenerates to a |-> (cond && b).
|
||||
assert property (@(posedge clk) disable iff (cyc < 10)
|
||||
a |-> (cond throughout b))
|
||||
else count_fail3 <= count_fail3 + 1;
|
||||
|
||||
always_ff @(posedge clk) begin
|
||||
`ifdef TEST_VERBOSE
|
||||
$write("[%0t] cyc==%0d crc=%x cond=%b a=%b b=%b\n",
|
||||
$time, cyc, crc, cond, a, b);
|
||||
`endif
|
||||
cyc <= cyc + 1;
|
||||
crc <= {crc[62:0], crc[63] ^ crc[2] ^ crc[0]};
|
||||
if (cyc == 0) begin
|
||||
crc <= 64'h5aef0c8d_d70a4497;
|
||||
end else if (cyc == 99) begin
|
||||
`checkh(crc, 64'hc77bb9b3784ea091);
|
||||
`checkd(count_fail1, 36);
|
||||
`checkd(count_fail2, 37);
|
||||
`checkd(count_fail3, 31);
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
end
|
||||
endmodule
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
%Error-UNSUPPORTED: t/t_sequence_sexpr_throughout_unsup.v:14:16: Unsupported: throughout with range delay sequence
|
||||
: ... note: In instance 't'
|
||||
14 | a |-> (a throughout (b ##[1:2] c)));
|
||||
| ^~~~~~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_throughout_unsup.v:18:16: Unsupported: throughout with complex sequence operator
|
||||
: ... note: In instance 't'
|
||||
18 | a |-> (a throughout ((b ##1 c) and (c ##1 b))));
|
||||
| ^~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_throughout_unsup.v:22:16: Unsupported: throughout with complex sequence operator
|
||||
: ... note: In instance 't'
|
||||
22 | a |-> (a throughout (b throughout (b ##1 c))));
|
||||
| ^~~~~~~~~~
|
||||
%Error: Exiting due to
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
#!/usr/bin/env python3
|
||||
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||
#
|
||||
# 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-FileCopyrightText: 2026 Wilson Snyder
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
import vltest_bootstrap
|
||||
|
||||
test.scenarios('vlt')
|
||||
|
||||
test.lint(expect_filename=test.golden_filename,
|
||||
verilator_flags2=['--assert --timing --error-limit 1000'],
|
||||
fails=True)
|
||||
|
||||
test.passes()
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 PlanV GmbH
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module t (
|
||||
input clk
|
||||
);
|
||||
logic a, b, c;
|
||||
|
||||
// Unsupported: throughout with range delay on RHS (IEEE 16.9.9)
|
||||
assert property (@(posedge clk)
|
||||
a |-> (a throughout (b ##[1:2] c)));
|
||||
|
||||
// Unsupported: throughout with temporal 'and' sequence on RHS
|
||||
assert property (@(posedge clk)
|
||||
a |-> (a throughout ((b ##1 c) and (c ##1 b))));
|
||||
|
||||
// Unsupported: nested throughout
|
||||
assert property (@(posedge clk)
|
||||
a |-> (a throughout (b throughout (b ##1 c))));
|
||||
|
||||
endmodule
|
||||
|
|
@ -2,24 +2,9 @@
|
|||
34 | a within(b);
|
||||
| ^~~~~~
|
||||
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:46:7: Unsupported: throughout (in sequence expression)
|
||||
46 | a throughout b;
|
||||
| ^~~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:50:7: Unsupported: intersect (in sequence expression)
|
||||
50 | a intersect b;
|
||||
| ^~~~~~~~~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:63:5: Unsupported: ## [*] cycle delay range expression
|
||||
63 | ## [*] b;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:66:5: Unsupported: ## [+] cycle delay range expression
|
||||
66 | ## [+] b;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:79:7: Unsupported: ## [*] cycle delay range expression
|
||||
79 | a ## [*] b;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:82:7: Unsupported: ## [+] cycle delay range expression
|
||||
82 | a ## [+] b;
|
||||
| ^~
|
||||
%Error-UNSUPPORTED: t/t_sequence_sexpr_unsup.v:95:7: Unsupported: [= boolean abbrev expression
|
||||
95 | a [= 1];
|
||||
| ^~
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ interface str_if;
|
|||
endinterface
|
||||
|
||||
module t;
|
||||
str_if sif();
|
||||
str_if sif ();
|
||||
virtual str_if vif = sif;
|
||||
|
||||
initial begin
|
||||
|
|
|
|||
|
|
@ -99,9 +99,9 @@ enum class Direction : uint8_t {
|
|||
};
|
||||
|
||||
#ifndef IVERILOG
|
||||
const std::array<TestSignal, 35> TestSignals = {
|
||||
const std::array<TestSignal, 36> TestSignals = {
|
||||
#else // Multidimensional packed arrays aren't tested in Icarus
|
||||
const std::array<TestSignal, 16> TestSignals = {
|
||||
const std::array<TestSignal, 17> TestSignals = {
|
||||
#endif
|
||||
TestSignal{"onebit",
|
||||
vpiIntVal,
|
||||
|
|
@ -149,6 +149,26 @@ const std::array<TestSignal, 16> TestSignals = {
|
|||
0}},
|
||||
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
||||
|
||||
TestSignal{"forcedNonForceable",
|
||||
vpiVectorVal,
|
||||
{},
|
||||
// NOLINTBEGIN (cppcoreguidelines-avoid-c-arrays)
|
||||
{.vector = (t_vpi_vecval[]){{0b10101010, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0b01010101, 0}}},
|
||||
true,
|
||||
{{.vector = (t_vpi_vecval[]){{0b10100101, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0b01011010, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0x5, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0xA, 0}}},
|
||||
{.lo = 0, .hi = 3}},
|
||||
{{.vector = (t_vpi_vecval[]){{0b10101011, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0b01010100, 0}}},
|
||||
vpiVectorVal,
|
||||
{.vector = (t_vpi_vecval[]){{0b1, 0}}},
|
||||
{.vector = (t_vpi_vecval[]){{0b0, 0}}},
|
||||
0}},
|
||||
// NOLINTEND (cppcoreguidelines-avoid-c-arrays)
|
||||
|
||||
TestSignal{
|
||||
"vectorQ",
|
||||
vpiVectorVal,
|
||||
|
|
|
|||
|
|
@ -130,6 +130,10 @@ typedef enum byte {
|
|||
// Verify that vpi_put_value still works with vpiInertialDelay
|
||||
logic [ 31:0] delayed `PUBLIC_FORCEABLE; // IData
|
||||
|
||||
// Verify that VPI still sees forced value if signal is forced through
|
||||
// SystemVerilog, but not marked as forceable
|
||||
logic [ 7:0] forcedNonForceable /*verilator public_flat_rw*/; // CData
|
||||
|
||||
// Clocked signals
|
||||
|
||||
// Force with vpiIntVal
|
||||
|
|
@ -208,6 +212,8 @@ typedef enum byte {
|
|||
|
||||
// Continuously assigned signals:
|
||||
|
||||
wire [ 7:0] forcedNonForceableContinuously /*verilator public_flat_rw*/; // CData
|
||||
|
||||
// Force with vpiIntVal
|
||||
wire onebitContinuously `PUBLIC_FORCEABLE; // CData
|
||||
wire [ 31:0] intvalContinuously `PUBLIC_FORCEABLE; // IData
|
||||
|
|
@ -282,6 +288,7 @@ typedef enum byte {
|
|||
|
||||
always @(posedge clk) begin
|
||||
nonPublic <= 1;
|
||||
forcedNonForceable <= 8'hAA;
|
||||
|
||||
onebit <= 1;
|
||||
intval <= 32'hAAAAAAAA;
|
||||
|
|
@ -356,6 +363,7 @@ typedef enum byte {
|
|||
ascPacked4dW <= '{'{'{'{32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA, 32'hAAAAAAAA}}}};
|
||||
`endif
|
||||
end
|
||||
assign forcedNonForceableContinuously = 8'hAA;
|
||||
|
||||
assign onebitContinuously = 1;
|
||||
assign intvalContinuously = 32'hAAAAAAAA;
|
||||
|
|
@ -431,6 +439,7 @@ typedef enum byte {
|
|||
`endif
|
||||
|
||||
task automatic svForceValues();
|
||||
force forcedNonForceable = 8'h55;
|
||||
force onebit = 0;
|
||||
force intval = 32'h55555555;
|
||||
force vectorC = 8'h55;
|
||||
|
|
@ -491,6 +500,7 @@ typedef enum byte {
|
|||
force ascPacked4dW[-3][2][-1][5] = 32'h55555555;
|
||||
`endif
|
||||
|
||||
force forcedNonForceableContinuously = 8'h55;
|
||||
force onebitContinuously = 0;
|
||||
force intvalContinuously = 32'h55555555;
|
||||
force vectorCContinuously = 8'h55;
|
||||
|
|
@ -550,6 +560,8 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svPartiallyForceValues();
|
||||
force forcedNonForceable[3:0] = 4'h5;
|
||||
|
||||
force intval[15:0] = 16'h5555;
|
||||
|
||||
force vectorC[3:0] = 4'h5;
|
||||
|
|
@ -585,6 +597,8 @@ typedef enum byte {
|
|||
`endif
|
||||
`endif
|
||||
|
||||
force forcedNonForceableContinuously[3:0] = 4'h5;
|
||||
|
||||
force intvalContinuously[15:0] = 16'h5555;
|
||||
|
||||
force vectorCContinuously[3:0] = 4'h5;
|
||||
|
|
@ -622,6 +636,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svForceSingleBit();
|
||||
force forcedNonForceable[0] = 1;
|
||||
force intval[0] = 1;
|
||||
force vectorC[0] = 1;
|
||||
force vectorQ[0] = 1;
|
||||
|
|
@ -650,6 +665,7 @@ typedef enum byte {
|
|||
force ascPacked4dW[-3][2][-1][5][39] = 1;
|
||||
`endif
|
||||
|
||||
force forcedNonForceableContinuously[0] = 1;
|
||||
force intvalContinuously[0] = 1;
|
||||
force vectorCContinuously[0] = 1;
|
||||
force vectorQContinuously[0] = 1;
|
||||
|
|
@ -861,6 +877,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svReleaseValues();
|
||||
release forcedNonForceable;
|
||||
release onebit;
|
||||
release intval;
|
||||
release vectorC;
|
||||
|
|
@ -899,6 +916,7 @@ typedef enum byte {
|
|||
release ascPacked4dW[-3][2][-1][5];
|
||||
`endif
|
||||
|
||||
release forcedNonForceableContinuously;
|
||||
release onebitContinuously;
|
||||
release intvalContinuously;
|
||||
release vectorCContinuously;
|
||||
|
|
@ -968,6 +986,8 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svPartiallyReleaseValues();
|
||||
release forcedNonForceable[3:0];
|
||||
|
||||
release intval[15:0];
|
||||
|
||||
release vectorC[3:0];
|
||||
|
|
@ -1001,6 +1021,8 @@ typedef enum byte {
|
|||
release ascPacked4dW[-3][2][-1][5][24:39];
|
||||
`endif
|
||||
|
||||
release forcedNonForceableContinuously[3:0];
|
||||
|
||||
release intvalContinuously[15:0];
|
||||
|
||||
release vectorCContinuously[3:0];
|
||||
|
|
@ -1095,6 +1117,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svReleaseSingleBit();
|
||||
release forcedNonForceable[0];
|
||||
release intval[0];
|
||||
release vectorC[0];
|
||||
release vectorQ[0];
|
||||
|
|
@ -1123,6 +1146,7 @@ typedef enum byte {
|
|||
release ascPacked4dW[-3][2][-1][5][39];
|
||||
`endif
|
||||
|
||||
release forcedNonForceableContinuously[0];
|
||||
release intvalContinuously[0];
|
||||
release vectorCContinuously[0];
|
||||
release vectorQContinuously[0];
|
||||
|
|
@ -1224,6 +1248,7 @@ typedef enum byte {
|
|||
task automatic svCheckValuesForced();
|
||||
svCheckNonContinuousValuesForced();
|
||||
|
||||
`checkh(forcedNonForceableContinuously, 8'h55);
|
||||
`checkh(onebitContinuously, 0);
|
||||
`checkh(intvalContinuously, 32'h55555555);
|
||||
`checkh(vectorCContinuously, 8'h55);
|
||||
|
|
@ -1290,6 +1315,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckNonContinuousValuesForced();
|
||||
`checkh(forcedNonForceable, 8'h55);
|
||||
`checkh(onebit, 0);
|
||||
`checkh(intval, 32'h55555555);
|
||||
`checkh(vectorC, 8'h55);
|
||||
|
|
@ -1355,6 +1381,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckContinuousValuesReleased();
|
||||
`checkh(forcedNonForceableContinuously, 8'hAA);
|
||||
`checkh(onebitContinuously, 1);
|
||||
`checkh(intvalContinuously, 32'hAAAAAAAA);
|
||||
`checkh(vectorCContinuously, 8'hAA);
|
||||
|
|
@ -1421,6 +1448,7 @@ typedef enum byte {
|
|||
task automatic svCheckValuesPartiallyForced();
|
||||
svCheckNonContinuousValuesPartiallyForced();
|
||||
|
||||
`checkh(forcedNonForceableContinuously, 8'h A5);
|
||||
`checkh(intvalContinuously, 32'hAAAA_5555);
|
||||
`checkh(vectorCContinuously, 8'h A5);
|
||||
`checkh(vectorQContinuously, 62'h2AAAAAAAD5555555);
|
||||
|
|
@ -1457,6 +1485,7 @@ typedef enum byte {
|
|||
task automatic svCheckSingleBitForced();
|
||||
svCheckNonContinuousSingleBitForced();
|
||||
|
||||
`checkh(forcedNonForceableContinuously, 8'hAB);
|
||||
`checkh(intvalContinuously, 32'hAAAAAAAB);
|
||||
`checkh(vectorCContinuously, 8'hAB);
|
||||
`checkh(vectorQContinuously, 62'h2AAAAAAA_AAAAAAAB);
|
||||
|
|
@ -1535,6 +1564,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckNonContinuousValuesPartiallyForced();
|
||||
`checkh(forcedNonForceable, 8'h A5);
|
||||
`checkh(intval, 32'hAAAA_5555);
|
||||
`checkh(vectorC, 8'h A5);
|
||||
`checkh(vectorQ, 62'h2AAAAAAAD5555555);
|
||||
|
|
@ -1593,6 +1623,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckNonContinuousSingleBitForced();
|
||||
`checkh(forcedNonForceable, 8'hAB);
|
||||
`checkh(intval, 32'hAAAAAAAB);
|
||||
`checkh(vectorC, 8'hAB);
|
||||
`checkh(vectorQ, 62'h2AAAAAAA_AAAAAAAB);
|
||||
|
|
@ -1648,6 +1679,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckValuesReleased();
|
||||
`checkh(forcedNonForceable, 8'hAA);
|
||||
`checkh(onebit, 1);
|
||||
`checkh(intval, 32'hAAAAAAAA);
|
||||
`checkh(vectorC, 8'hAA);
|
||||
|
|
@ -1714,6 +1746,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckValuesPartiallyReleased();
|
||||
`checkh(forcedNonForceable, 'h5a);
|
||||
`checkh(intval, 'h5555aaaa);
|
||||
`checkh(vectorC, 'h5a);
|
||||
`checkh(vectorQ, 'h155555552aaaaaaa);
|
||||
|
|
@ -1750,6 +1783,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckContinuousValuesPartiallyReleased();
|
||||
`checkh(forcedNonForceableContinuously, 'h5a);
|
||||
`checkh(intvalContinuously, 'h5555aaaa);
|
||||
`checkh(vectorCContinuously, 'h5a);
|
||||
`checkh(vectorQContinuously, 'h155555552aaaaaaa);
|
||||
|
|
@ -1830,6 +1864,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckContinuousValuesSingleBitReleased();
|
||||
`checkh(forcedNonForceableContinuously, 8'h54);
|
||||
`checkh(intvalContinuously, 32'h55555554);
|
||||
`checkh(vectorCContinuously, 8'h54);
|
||||
`checkh(vectorQContinuously, 62'h15555555_55555554);
|
||||
|
|
@ -1886,6 +1921,7 @@ typedef enum byte {
|
|||
endtask
|
||||
|
||||
task automatic svCheckSingleBitReleased();
|
||||
`checkh(forcedNonForceable, 8'h54);
|
||||
`checkh(intval, 32'h55555554);
|
||||
`checkh(vectorC, 8'h54);
|
||||
`checkh(vectorQ, 62'h15555555_55555554);
|
||||
|
|
@ -2443,6 +2479,7 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE));
|
|||
$display("time: %0t\tclk:%b", $time, clk);
|
||||
|
||||
$display("nonPublic: %x", nonPublic);
|
||||
$display("forcedNonForceable: %x", forcedNonForceable);
|
||||
|
||||
$display("str1: %s", str1);
|
||||
$display("delayed: %x", delayed);
|
||||
|
|
@ -2484,6 +2521,7 @@ $dumpfile(`STRINGIFY(`TEST_DUMPFILE));
|
|||
$display("ascPacked4dQ: %x", ascPacked4dQ);
|
||||
$display("ascPacked4dW: %x", ascPacked4dW);
|
||||
|
||||
$display("forcedNonForceableContinuously: %x", forcedNonForceableContinuously);
|
||||
$display("onebitContinuously: %x", onebitContinuously);
|
||||
$display("intvalContinuously: %x", intvalContinuously);
|
||||
$display("vectorCContinuously: %x", vectorCContinuously);
|
||||
|
|
|
|||
Loading…
Reference in New Issue