Support clocking blocks (#3674)
This commit is contained in:
parent
d64971ba35
commit
bb44d4e4f2
|
|
@ -1238,6 +1238,42 @@ static inline bool VL_CAST_DYNAMIC(VlClassRef<T> in, VlClassRef<U>& outr) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
// VlSampleQueue stores samples for input clockvars in clocking blocks. At a clocking event,
|
||||||
|
// samples from this queue should be written to the correct input clockvar.
|
||||||
|
|
||||||
|
template <typename T_Sampled>
|
||||||
|
class VlSampleQueue final {
|
||||||
|
// TYPES
|
||||||
|
// Type representing a single value sample at a point in time
|
||||||
|
struct VlSample {
|
||||||
|
uint64_t m_timestamp; // Timestamp at which the value was sampled
|
||||||
|
T_Sampled m_value; // The sampled value
|
||||||
|
};
|
||||||
|
|
||||||
|
// MEMBERS
|
||||||
|
std::deque<VlSample> m_queue; // Queue of samples with timestamps
|
||||||
|
|
||||||
|
public:
|
||||||
|
// METHODS
|
||||||
|
// Push a new sample with the given timestamp to the end of the queue
|
||||||
|
void push(uint64_t time, const T_Sampled& value) { m_queue.push_back({time, value}); }
|
||||||
|
// Get the latest sample with its timestamp less than or equal to the given skew
|
||||||
|
void pop(uint64_t time, uint64_t skew, T_Sampled& value) {
|
||||||
|
if (time < skew) return;
|
||||||
|
// Find the last element not greater than (time - skew). Do a binary search, as the queue
|
||||||
|
// should be ordered.
|
||||||
|
auto it = std::lower_bound(m_queue.rbegin(), m_queue.rend(), VlSample{time - skew, {}},
|
||||||
|
[](const VlSample& sample, const VlSample& skewed) {
|
||||||
|
return sample.m_timestamp > skewed.m_timestamp;
|
||||||
|
});
|
||||||
|
if (it != m_queue.rend()) {
|
||||||
|
value = it->m_value;
|
||||||
|
m_queue.erase(m_queue.begin(), it.base());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
//======================================================================
|
//======================================================================
|
||||||
|
|
||||||
#define VL_NEW(Class, ...) \
|
#define VL_NEW(Class, ...) \
|
||||||
|
|
|
||||||
|
|
@ -243,16 +243,21 @@ public:
|
||||||
// METHODS
|
// METHODS
|
||||||
AstScope* scopep() { return m_scopep; }
|
AstScope* scopep() { return m_scopep; }
|
||||||
|
|
||||||
// Make a new AstActive sensitive to the given special sensitivity class and return it
|
// Make a new AstActive sensitive to the given sentree and return it
|
||||||
template <typename SenItemKind>
|
AstActive* makeActive(FileLine* const fl, AstSenTree* const senTreep) {
|
||||||
AstActive* makeSpecialActive(FileLine* const fl) {
|
|
||||||
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, SenItemKind{}}};
|
|
||||||
auto* const activep = new AstActive{fl, "", senTreep};
|
auto* const activep = new AstActive{fl, "", senTreep};
|
||||||
activep->sensesStorep(activep->sensesp());
|
activep->sensesStorep(activep->sensesp());
|
||||||
addActive(activep);
|
addActive(activep);
|
||||||
return activep;
|
return activep;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Make a new AstActive sensitive to the given special sensitivity class and return it
|
||||||
|
template <typename SenItemKind>
|
||||||
|
AstActive* makeSpecialActive(FileLine* const fl) {
|
||||||
|
AstSenTree* const senTreep = new AstSenTree{fl, new AstSenItem{fl, SenItemKind{}}};
|
||||||
|
return makeActive(fl, senTreep);
|
||||||
|
}
|
||||||
|
|
||||||
// Return an AstActive sensitive to the given special sensitivity class (possibly pre-created)
|
// Return an AstActive sensitive to the given special sensitivity class (possibly pre-created)
|
||||||
template <typename SenItemKind>
|
template <typename SenItemKind>
|
||||||
AstActive* getSpecialActive(FileLine* fl) {
|
AstActive* getSpecialActive(FileLine* fl) {
|
||||||
|
|
@ -542,6 +547,22 @@ private:
|
||||||
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
|
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
|
||||||
activep->addStmtsp(nodep->unlinkFrBack());
|
activep->addStmtsp(nodep->unlinkFrBack());
|
||||||
}
|
}
|
||||||
|
void visit(AstAlwaysObserved* nodep) override {
|
||||||
|
UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree");
|
||||||
|
AstSenTree* const sensesp = nodep->sensesp();
|
||||||
|
sensesp->unlinkFrBack();
|
||||||
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
||||||
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp);
|
||||||
|
activep->addStmtsp(nodep->unlinkFrBack());
|
||||||
|
}
|
||||||
|
void visit(AstAlwaysReactive* nodep) override {
|
||||||
|
UASSERT_OBJ(nodep->sensesp(), nodep, "Should have a sentree");
|
||||||
|
AstSenTree* const sensesp = nodep->sensesp();
|
||||||
|
sensesp->unlinkFrBack();
|
||||||
|
// Make a new active for it, needs to be the only item under the active for V3Sched
|
||||||
|
AstActive* const activep = m_namer.makeActive(nodep->fileline(), sensesp);
|
||||||
|
activep->addStmtsp(nodep->unlinkFrBack());
|
||||||
|
}
|
||||||
void visit(AstAlwaysPublic* nodep) override {
|
void visit(AstAlwaysPublic* nodep) override {
|
||||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,7 @@
|
||||||
// Pre steps:
|
// Pre steps:
|
||||||
// Attach clocks to each assertion
|
// Attach clocks to each assertion
|
||||||
// Substitute property references by property body (IEEE Std 1800-2012, section 16.12.1).
|
// Substitute property references by property body (IEEE Std 1800-2012, section 16.12.1).
|
||||||
|
// Transform clocking blocks into imperative logic
|
||||||
//*************************************************************************
|
//*************************************************************************
|
||||||
|
|
||||||
#include "config_build.h"
|
#include "config_build.h"
|
||||||
|
|
@ -24,8 +25,10 @@
|
||||||
#include "V3AssertPre.h"
|
#include "V3AssertPre.h"
|
||||||
|
|
||||||
#include "V3Ast.h"
|
#include "V3Ast.h"
|
||||||
|
#include "V3Const.h"
|
||||||
#include "V3Global.h"
|
#include "V3Global.h"
|
||||||
#include "V3Task.h"
|
#include "V3Task.h"
|
||||||
|
#include "V3UniqueNames.h"
|
||||||
|
|
||||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||||
|
|
||||||
|
|
@ -37,16 +40,26 @@ class AssertPreVisitor final : public VNVisitor {
|
||||||
// Eventually inlines calls to sequences, properties, etc.
|
// Eventually inlines calls to sequences, properties, etc.
|
||||||
// We're not parsing the tree, or anything more complicated.
|
// We're not parsing the tree, or anything more complicated.
|
||||||
private:
|
private:
|
||||||
// NODE STATE/TYPES
|
// NODE STATE
|
||||||
|
const VNUser1InUse m_inuser1;
|
||||||
// STATE
|
// STATE
|
||||||
|
// Current context:
|
||||||
|
AstNetlist* const m_netlistp = nullptr; // Current netlist
|
||||||
|
AstNodeModule* m_modp = nullptr; // Current module
|
||||||
|
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||||
// Reset each module:
|
// Reset each module:
|
||||||
AstSenItem* m_seniDefaultp = nullptr; // Default sensitivity (from AstDefClock)
|
AstClocking* m_defaultClockingp = nullptr; // Default clocking for the current module
|
||||||
// Reset each assertion:
|
// Reset each assertion:
|
||||||
AstSenItem* m_senip = nullptr; // Last sensitivity
|
AstSenItem* m_senip = nullptr; // Last sensitivity
|
||||||
// Reset each always:
|
// Reset each always:
|
||||||
AstSenItem* m_seniAlwaysp = nullptr; // Last sensitivity in always
|
AstSenItem* m_seniAlwaysp = nullptr; // Last sensitivity in always
|
||||||
// Reset each assertion:
|
// Reset each assertion:
|
||||||
AstNodeExpr* m_disablep = nullptr; // Last disable
|
AstNodeExpr* m_disablep = nullptr; // Last disable
|
||||||
|
// Other:
|
||||||
|
V3UniqueNames m_cycleDlyNames{"__VcycleDly"}; // Cycle delay counter name generator
|
||||||
|
bool m_inAssign = false; // True if in an AssignNode
|
||||||
|
bool m_inAssignDlyLhs = false; // True if in AssignDly's LHS
|
||||||
|
bool m_inSynchDrive = false; // True if in synchronous drive
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
|
@ -55,7 +68,7 @@ private:
|
||||||
// Return nullptr for always
|
// Return nullptr for always
|
||||||
AstSenTree* newp = nullptr;
|
AstSenTree* newp = nullptr;
|
||||||
AstSenItem* senip = m_senip;
|
AstSenItem* senip = m_senip;
|
||||||
if (!senip) senip = m_seniDefaultp;
|
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
|
||||||
if (!senip) senip = m_seniAlwaysp;
|
if (!senip) senip = m_seniAlwaysp;
|
||||||
if (!senip) {
|
if (!senip) {
|
||||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
||||||
|
|
@ -139,17 +152,199 @@ private:
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
//========== Statements
|
//========== Statements
|
||||||
void visit(AstClocking* nodep) override {
|
void visit(AstClocking* const nodep) override {
|
||||||
|
VL_RESTORER(m_clockingp);
|
||||||
|
m_clockingp = nodep;
|
||||||
UINFO(8, " CLOCKING" << nodep << endl);
|
UINFO(8, " CLOCKING" << nodep << endl);
|
||||||
// Store the new default clock, reset on new module
|
iterateChildren(nodep);
|
||||||
m_seniDefaultp = nodep->sensesp();
|
}
|
||||||
// Trash it, keeping children
|
void visit(AstClockingItem* const nodep) override {
|
||||||
if (nodep->bodysp()) {
|
FileLine* const flp = nodep->fileline();
|
||||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
V3Const::constifyEdit(nodep->skewp());
|
||||||
} else {
|
if (!VN_IS(nodep->skewp(), Const)) {
|
||||||
nodep->unlinkFrBack();
|
nodep->skewp()->v3error("Skew must be constant (IEEE 1800-2017 14.4)");
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
||||||
|
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
||||||
|
AstNodeExpr* const exprp = nodep->exprp();
|
||||||
|
// Get a ref to the sampled/driven variable
|
||||||
|
AstVar* const varp = nodep->varp()->unlinkFrBack();
|
||||||
|
m_clockingp->addVarsp(varp);
|
||||||
|
varp->user1p(nodep);
|
||||||
|
if (nodep->direction() == VDirection::OUTPUT) {
|
||||||
|
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||||
|
skewedRefp->user1(true);
|
||||||
|
AstAssign* const assignp = new AstAssign{flp, exprp->cloneTree(false), skewedRefp};
|
||||||
|
if (skewp->isZero()) {
|
||||||
|
// Drive the var in Re-NBA (IEEE 1800-2017 14.16)
|
||||||
|
m_clockingp->addNextHere(new AstAlwaysReactive{
|
||||||
|
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||||
|
} else if (skewp->fileline()->timingOn()) {
|
||||||
|
// Create a fork so that this AlwaysObserved can be retriggered before the
|
||||||
|
// assignment happens. Also then it can be combo, avoiding the need for creating
|
||||||
|
// new triggers.
|
||||||
|
AstFork* const forkp = new AstFork{flp, "", assignp};
|
||||||
|
forkp->joinType(VJoinType::JOIN_NONE);
|
||||||
|
// Use Observed for this to make sure we do not miss the event
|
||||||
|
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||||
|
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, forkp});
|
||||||
|
if (v3Global.opt.timing().isSetTrue()) {
|
||||||
|
assignp->timingControlp(new AstDelay{flp, skewp->unlinkFrBack(), false});
|
||||||
|
} else if (v3Global.opt.timing().isSetFalse()) {
|
||||||
|
nodep->v3warn(E_NOTIMING,
|
||||||
|
"Clocking output skew greater than #0 requires --timing");
|
||||||
|
} else {
|
||||||
|
nodep->v3warn(E_NEEDTIMINGOPT,
|
||||||
|
"Use --timing or --no-timing to specify how "
|
||||||
|
"clocking output skew greater than #0 should be handled");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nodep->direction() == VDirection::INPUT) {
|
||||||
|
// Ref to the clockvar
|
||||||
|
AstVarRef* const refp = new AstVarRef{flp, varp, VAccess::WRITE};
|
||||||
|
refp->user1(true);
|
||||||
|
if (skewp->num().is1Step()) {
|
||||||
|
// Assign the sampled expression to the clockvar (IEEE 1800-2017 14.13)
|
||||||
|
AstSampled* const sampledp = new AstSampled{flp, exprp->cloneTree(false)};
|
||||||
|
sampledp->dtypeFrom(exprp);
|
||||||
|
m_clockingp->addNextHere(new AstAssignW{flp, refp, sampledp});
|
||||||
|
} else if (skewp->isZero()) {
|
||||||
|
// #0 means the var has to be sampled in Observed (IEEE 1800-2017 14.13)
|
||||||
|
AstAssign* const assignp = new AstAssign{flp, refp, exprp->cloneTree(false)};
|
||||||
|
m_clockingp->addNextHere(new AstAlwaysObserved{
|
||||||
|
flp, new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)}, assignp});
|
||||||
|
} else {
|
||||||
|
// Create a queue where we'll store sampled values with timestamps
|
||||||
|
AstSampleQueueDType* const queueDtp
|
||||||
|
= new AstSampleQueueDType{flp, exprp->dtypep()};
|
||||||
|
m_netlistp->typeTablep()->addTypesp(queueDtp);
|
||||||
|
AstVar* const queueVarp = new AstVar{
|
||||||
|
flp, VVarType::MODULETEMP,
|
||||||
|
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
|
||||||
|
m_clockingp->addNextHere(queueVarp);
|
||||||
|
// Create a process like this:
|
||||||
|
// always queue.push(<sampled var>);
|
||||||
|
AstCMethodHard* const pushp = new AstCMethodHard{
|
||||||
|
flp, new AstVarRef{flp, queueVarp, VAccess::WRITE}, "push",
|
||||||
|
new AstTime(nodep->fileline(), m_modp->timeunit())};
|
||||||
|
pushp->addPinsp(exprp->cloneTree(false));
|
||||||
|
pushp->dtypeSetVoid();
|
||||||
|
m_clockingp->addNextHere(
|
||||||
|
new AstAlways{flp, VAlwaysKwd::ALWAYS, nullptr, pushp->makeStmt()});
|
||||||
|
// Create a process like this:
|
||||||
|
// always @<clocking event> queue.pop(<skew>, /*out*/<skewed var>});
|
||||||
|
AstCMethodHard* const popp = new AstCMethodHard{
|
||||||
|
flp, new AstVarRef{flp, queueVarp, VAccess::READWRITE}, "pop",
|
||||||
|
new AstTime(nodep->fileline(), m_modp->timeunit())};
|
||||||
|
popp->addPinsp(skewp->unlinkFrBack());
|
||||||
|
popp->addPinsp(refp);
|
||||||
|
popp->dtypeSetVoid();
|
||||||
|
m_clockingp->addNextHere(
|
||||||
|
new AstAlways{flp, VAlwaysKwd::ALWAYS,
|
||||||
|
new AstSenTree{flp, m_clockingp->sensesp()->cloneTree(false)},
|
||||||
|
popp->makeStmt()});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
nodep->v3fatal("Invalid direction");
|
||||||
|
}
|
||||||
|
pushDeletep(nodep->unlinkFrBack());
|
||||||
|
}
|
||||||
|
void visit(AstDelay* nodep) override {
|
||||||
|
// Only cycle delays are relevant in this stage; also only process once
|
||||||
|
if (!nodep->isCycleDelay()) {
|
||||||
|
if (m_inSynchDrive) {
|
||||||
|
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
||||||
|
" (IEEE 1800-2017 14.16)");
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (m_inAssign && !m_inSynchDrive) {
|
||||||
|
nodep->v3error("Cycle delays not allowed as intra-assignment delays"
|
||||||
|
" (IEEE 1800-2017 14.11)");
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (nodep->stmtsp()) nodep->addNextHere(nodep->stmtsp()->unlinkFrBackWithNext());
|
||||||
|
FileLine* const flp = nodep->fileline();
|
||||||
|
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||||
|
AstConst* const constp = VN_CAST(valuep, Const);
|
||||||
|
if (constp->isZero()) {
|
||||||
|
nodep->v3warn(E_UNSUPPORTED, "Unsupported: ##0 delays");
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!m_defaultClockingp) {
|
||||||
|
nodep->v3error("Usage of cycle delays requires default clocking"
|
||||||
|
" (IEEE 1800-2017 14.11)");
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
AstEventControl* const controlp = new AstEventControl{
|
||||||
|
nodep->fileline(),
|
||||||
|
new AstSenTree{flp, m_defaultClockingp->sensesp()->cloneTree(false)}, nullptr};
|
||||||
|
const std::string delayName = m_cycleDlyNames.get(nodep);
|
||||||
|
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
|
||||||
|
nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
|
||||||
|
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true};
|
||||||
|
beginp->addStmtsp(new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
|
||||||
|
beginp->addStmtsp(new AstWhile{
|
||||||
|
nodep->fileline(),
|
||||||
|
new AstGt{flp, new AstVarRef{flp, cntVarp, VAccess::READ}, new AstConst{flp, 0}},
|
||||||
|
controlp,
|
||||||
|
new AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE},
|
||||||
|
new AstSub{flp, new AstVarRef{flp, cntVarp, VAccess::READ},
|
||||||
|
new AstConst{flp, 1}}}});
|
||||||
|
nodep->replaceWith(beginp);
|
||||||
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
|
}
|
||||||
|
void visit(AstSenTree* nodep) override {
|
||||||
|
if (m_inSynchDrive) {
|
||||||
|
nodep->v3error("Event controls cannot be used in "
|
||||||
|
"synchronous drives (IEEE 1800-2017 14.16)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void visit(AstNodeVarRef* nodep) override {
|
||||||
|
if (AstClockingItem* const itemp = VN_CAST(nodep->varp()->user1p(), ClockingItem)) {
|
||||||
|
if (nodep->access().isReadOrRW() && !nodep->user1()
|
||||||
|
&& itemp->direction() == VDirection::OUTPUT) {
|
||||||
|
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2017 14.3)");
|
||||||
|
}
|
||||||
|
if (nodep->access().isWriteOrRW()) {
|
||||||
|
if (itemp->direction() == VDirection::OUTPUT) {
|
||||||
|
if (!m_inAssignDlyLhs) {
|
||||||
|
nodep->v3error("Only non-blocking assignments can write "
|
||||||
|
"to clockvars (IEEE 1800-2017 14.16)");
|
||||||
|
}
|
||||||
|
if (m_inAssign) m_inSynchDrive = true;
|
||||||
|
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
|
||||||
|
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2017 14.3)");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void visit(AstNodeAssign* nodep) override {
|
||||||
|
if (nodep->user1()) return;
|
||||||
|
VL_RESTORER(m_inAssign);
|
||||||
|
VL_RESTORER(m_inSynchDrive);
|
||||||
|
m_inAssign = true;
|
||||||
|
m_inSynchDrive = false;
|
||||||
|
{
|
||||||
|
VL_RESTORER(m_inAssignDlyLhs);
|
||||||
|
m_inAssignDlyLhs = VN_IS(nodep, AssignDly);
|
||||||
|
iterate(nodep->lhsp());
|
||||||
|
}
|
||||||
|
iterate(nodep->rhsp());
|
||||||
|
if (nodep->timingControlp()) {
|
||||||
|
iterate(nodep->timingControlp());
|
||||||
|
} else if (m_inSynchDrive) {
|
||||||
|
AstAssign* const assignp = new AstAssign{
|
||||||
|
nodep->fileline(), nodep->lhsp()->unlinkFrBack(), nodep->rhsp()->unlinkFrBack()};
|
||||||
|
assignp->user1(true);
|
||||||
|
nodep->replaceWith(assignp);
|
||||||
|
VL_DO_DANGLING(nodep->deleteTree(), nodep);
|
||||||
}
|
}
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
|
||||||
}
|
}
|
||||||
void visit(AstAlways* nodep) override {
|
void visit(AstAlways* nodep) override {
|
||||||
iterateAndNextNull(nodep->sensesp());
|
iterateAndNextNull(nodep->sensesp());
|
||||||
|
|
@ -255,9 +450,20 @@ private:
|
||||||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
|
VL_RESTORER(m_defaultClockingp);
|
||||||
|
VL_RESTORER(m_modp);
|
||||||
|
m_defaultClockingp = nullptr;
|
||||||
|
nodep->foreach([&](AstClocking* const clockingp) {
|
||||||
|
if (clockingp->isDefault()) {
|
||||||
|
if (m_defaultClockingp) {
|
||||||
|
clockingp->v3error("Only one default clocking block allowed per module"
|
||||||
|
" (IEEE 1800-2017 14.12)");
|
||||||
|
}
|
||||||
|
m_defaultClockingp = clockingp;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
m_modp = nodep;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
// Reset defaults
|
|
||||||
m_seniDefaultp = nullptr;
|
|
||||||
}
|
}
|
||||||
void visit(AstProperty* nodep) override {
|
void visit(AstProperty* nodep) override {
|
||||||
// The body will be visited when will be substituted in place of property reference
|
// The body will be visited when will be substituted in place of property reference
|
||||||
|
|
@ -268,7 +474,8 @@ private:
|
||||||
|
|
||||||
public:
|
public:
|
||||||
// CONSTRUCTORS
|
// CONSTRUCTORS
|
||||||
explicit AssertPreVisitor(AstNetlist* nodep) {
|
explicit AssertPreVisitor(AstNetlist* nodep)
|
||||||
|
: m_netlistp{nodep} {
|
||||||
clearAssertInfo();
|
clearAssertInfo();
|
||||||
// Process
|
// Process
|
||||||
iterate(nodep);
|
iterate(nodep);
|
||||||
|
|
|
||||||
|
|
@ -1122,6 +1122,50 @@ public:
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
class AstSampleQueueDType final : public AstNodeDType {
|
||||||
|
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
||||||
|
AstNodeDType* m_refDTypep = nullptr; // Elements of this type (after widthing)
|
||||||
|
public:
|
||||||
|
AstSampleQueueDType(FileLine* fl, AstNodeDType* dtp)
|
||||||
|
: ASTGEN_SUPER_SampleQueueDType(fl) {
|
||||||
|
refDTypep(dtp);
|
||||||
|
dtypep(dtp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstSampleQueueDType;
|
||||||
|
const char* broken() const override {
|
||||||
|
BROKEN_RTN(!((m_refDTypep && !childDTypep() && m_refDTypep->brokeExists())
|
||||||
|
|| (!m_refDTypep && childDTypep())));
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
void cloneRelink() override {
|
||||||
|
if (m_refDTypep && m_refDTypep->clonep()) m_refDTypep = m_refDTypep->clonep();
|
||||||
|
}
|
||||||
|
bool same(const AstNode* samep) const override {
|
||||||
|
const AstNodeArrayDType* const asamep = static_cast<const AstNodeArrayDType*>(samep);
|
||||||
|
if (!asamep->subDTypep()) return false;
|
||||||
|
return (subDTypep() == asamep->subDTypep());
|
||||||
|
}
|
||||||
|
bool similarDType(const AstNodeDType* samep) const override {
|
||||||
|
const AstSampleQueueDType* const asamep = static_cast<const AstSampleQueueDType*>(samep);
|
||||||
|
return type() == samep->type() && asamep->subDTypep()
|
||||||
|
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||||
|
}
|
||||||
|
void dumpSmall(std::ostream& str) const override;
|
||||||
|
AstNodeDType* getChildDTypep() const override { return childDTypep(); }
|
||||||
|
// op1 = Range of variable
|
||||||
|
AstNodeDType* subDTypep() const override { return m_refDTypep ? m_refDTypep : childDTypep(); }
|
||||||
|
void refDTypep(AstNodeDType* nodep) { m_refDTypep = nodep; }
|
||||||
|
AstNodeDType* virtRefDTypep() const override { return m_refDTypep; }
|
||||||
|
void virtRefDTypep(AstNodeDType* nodep) override { refDTypep(nodep); }
|
||||||
|
// METHODS
|
||||||
|
AstBasicDType* basicp() const override { return subDTypep()->basicp(); }
|
||||||
|
AstNodeDType* skipRefp() const override { return (AstNodeDType*)this; }
|
||||||
|
AstNodeDType* skipRefToConstp() const override { return (AstNodeDType*)this; }
|
||||||
|
AstNodeDType* skipRefToEnump() const override { return (AstNodeDType*)this; }
|
||||||
|
int widthAlignBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||||
|
int widthTotalBytes() const override { return sizeof(std::map<std::string, std::string>); }
|
||||||
|
bool isCompound() const override { return true; }
|
||||||
|
};
|
||||||
class AstUnsizedArrayDType final : public AstNodeDType {
|
class AstUnsizedArrayDType final : public AstNodeDType {
|
||||||
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
||||||
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
// @astgen op1 := childDTypep : Optional[AstNodeDType] // moved to refDTypep() in V3Width
|
||||||
|
|
|
||||||
|
|
@ -936,6 +936,13 @@ public:
|
||||||
dtypeSetBit(); // Events 1 bit, objects 64 bits, so autoExtend=1 and use bit here
|
dtypeSetBit(); // Events 1 bit, objects 64 bits, so autoExtend=1 and use bit here
|
||||||
initWithNumber();
|
initWithNumber();
|
||||||
}
|
}
|
||||||
|
class OneStep {};
|
||||||
|
AstConst(FileLine* fl, OneStep)
|
||||||
|
: ASTGEN_SUPER_Const(fl)
|
||||||
|
, m_num(V3Number::OneStep{}, this) {
|
||||||
|
dtypeSetLogicSized(64, VSigning::UNSIGNED);
|
||||||
|
initWithNumber();
|
||||||
|
}
|
||||||
ASTGEN_MEMBERS_AstConst;
|
ASTGEN_MEMBERS_AstConst;
|
||||||
string name() const override { return num().ascii(); } // * = Value
|
string name() const override { return num().ascii(); } // * = Value
|
||||||
const V3Number& num() const VL_MT_SAFE { return m_num; } // * = Value
|
const V3Number& num() const VL_MT_SAFE { return m_num; } // * = Value
|
||||||
|
|
|
||||||
|
|
@ -814,17 +814,50 @@ public:
|
||||||
AstClass* classp() const; // Class being extended (after link)
|
AstClass* classp() const; // Class being extended (after link)
|
||||||
};
|
};
|
||||||
class AstClocking final : public AstNode {
|
class AstClocking final : public AstNode {
|
||||||
// Set default clock region
|
|
||||||
// Parents: MODULE
|
// Parents: MODULE
|
||||||
// @astgen op1 := sensesp : List[AstSenItem]
|
// Children: SENITEM, CLOCKING ITEMs, VARs
|
||||||
// @astgen op2 := bodysp : List[AstNode]
|
// @astgen op1 := sensesp : AstSenItem
|
||||||
|
// @astgen op2 := itemsp : List[AstClockingItem]
|
||||||
|
// @astgen op3 := varsp : List[AstVar]
|
||||||
|
// @astgen op4 := eventp : Optional[AstVar]
|
||||||
|
std::string m_name; // Clocking block name
|
||||||
|
const bool m_isDefault = false; // True if default clocking
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstClocking(FileLine* fl, AstSenItem* sensesp, AstNode* bodysp)
|
AstClocking(FileLine* fl, const std::string& name, AstSenItem* sensesp,
|
||||||
: ASTGEN_SUPER_Clocking(fl) {
|
AstClockingItem* itemsp, bool isDefault)
|
||||||
this->addSensesp(sensesp);
|
: ASTGEN_SUPER_Clocking(fl)
|
||||||
this->addBodysp(bodysp);
|
, m_isDefault{isDefault} {
|
||||||
|
m_name = name;
|
||||||
|
this->sensesp(sensesp);
|
||||||
|
addItemsp(itemsp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstClocking;
|
ASTGEN_MEMBERS_AstClocking;
|
||||||
|
std::string name() const override { return m_name; }
|
||||||
|
bool isDefault() const { return m_isDefault; }
|
||||||
|
};
|
||||||
|
class AstClockingItem final : public AstNode {
|
||||||
|
// Parents: CLOCKING
|
||||||
|
// Children: EXPRs, ASSIGNs, VARs
|
||||||
|
// @astgen op1 := skewp : Optional[AstNodeExpr]
|
||||||
|
// @astgen op2 := exprp : Optional[AstNodeExpr]
|
||||||
|
// @astgen op3 := assignp : Optional[AstAssign]
|
||||||
|
// @astgen op4 := varp : Optional[AstVar]
|
||||||
|
VDirection m_direction;
|
||||||
|
|
||||||
|
public:
|
||||||
|
AstClockingItem(FileLine* fl, VDirection direction, AstNodeExpr* skewp, AstNode* clockingDeclp)
|
||||||
|
: ASTGEN_SUPER_ClockingItem(fl) {
|
||||||
|
m_direction = direction;
|
||||||
|
this->skewp(skewp);
|
||||||
|
if (AstAssign* const assignp = VN_CAST(clockingDeclp, Assign)) {
|
||||||
|
this->assignp(assignp);
|
||||||
|
} else {
|
||||||
|
exprp(VN_AS(clockingDeclp, NodeExpr));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstClockingItem;
|
||||||
|
VDirection direction() const { return m_direction; }
|
||||||
};
|
};
|
||||||
class AstConstPool final : public AstNode {
|
class AstConstPool final : public AstNode {
|
||||||
// Container for const static data
|
// Container for const static data
|
||||||
|
|
@ -2182,6 +2215,17 @@ public:
|
||||||
void dump(std::ostream& str) const override;
|
void dump(std::ostream& str) const override;
|
||||||
VAlwaysKwd keyword() const { return m_keyword; }
|
VAlwaysKwd keyword() const { return m_keyword; }
|
||||||
};
|
};
|
||||||
|
class AstAlwaysObserved final : public AstNodeProcedure {
|
||||||
|
// Like always but Observed scheduling region
|
||||||
|
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list, removed in V3Active
|
||||||
|
|
||||||
|
public:
|
||||||
|
AstAlwaysObserved(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp)
|
||||||
|
: ASTGEN_SUPER_AlwaysObserved(fl, bodysp) {
|
||||||
|
this->sensesp(sensesp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstAlwaysObserved;
|
||||||
|
};
|
||||||
class AstAlwaysPost final : public AstNodeProcedure {
|
class AstAlwaysPost final : public AstNodeProcedure {
|
||||||
// Like always but post assignments for memory assignment IFs
|
// Like always but post assignments for memory assignment IFs
|
||||||
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
||||||
|
|
@ -2200,6 +2244,17 @@ public:
|
||||||
: ASTGEN_SUPER_AlwaysPostponed(fl, stmtsp) {}
|
: ASTGEN_SUPER_AlwaysPostponed(fl, stmtsp) {}
|
||||||
ASTGEN_MEMBERS_AstAlwaysPostponed;
|
ASTGEN_MEMBERS_AstAlwaysPostponed;
|
||||||
};
|
};
|
||||||
|
class AstAlwaysReactive final : public AstNodeProcedure {
|
||||||
|
// Like always but Reactive scheduling region
|
||||||
|
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list, removed in V3Active
|
||||||
|
|
||||||
|
public:
|
||||||
|
AstAlwaysReactive(FileLine* fl, AstSenTree* sensesp, AstNode* bodysp)
|
||||||
|
: ASTGEN_SUPER_AlwaysReactive(fl, bodysp) {
|
||||||
|
this->sensesp(sensesp);
|
||||||
|
}
|
||||||
|
ASTGEN_MEMBERS_AstAlwaysReactive;
|
||||||
|
};
|
||||||
class AstFinal final : public AstNodeProcedure {
|
class AstFinal final : public AstNodeProcedure {
|
||||||
public:
|
public:
|
||||||
AstFinal(FileLine* fl, AstNode* stmtsp)
|
AstFinal(FileLine* fl, AstNode* stmtsp)
|
||||||
|
|
@ -2496,13 +2551,18 @@ class AstDelay final : public AstNodeStmt {
|
||||||
// Delay statement
|
// Delay statement
|
||||||
// @astgen op1 := lhsp : AstNodeExpr // Delay value
|
// @astgen op1 := lhsp : AstNodeExpr // Delay value
|
||||||
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
|
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
|
||||||
|
const bool m_isCycle; // True if it is a cycle delay
|
||||||
|
|
||||||
public:
|
public:
|
||||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp)
|
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
|
||||||
: ASTGEN_SUPER_Delay(fl) {
|
: ASTGEN_SUPER_Delay(fl)
|
||||||
|
, m_isCycle{isCycle} {
|
||||||
this->lhsp(lhsp);
|
this->lhsp(lhsp);
|
||||||
}
|
}
|
||||||
ASTGEN_MEMBERS_AstDelay;
|
ASTGEN_MEMBERS_AstDelay;
|
||||||
|
void dump(std::ostream& str) const override;
|
||||||
bool isTimingControl() const override { return true; }
|
bool isTimingControl() const override { return true; }
|
||||||
|
bool isCycleDelay() const { return m_isCycle; }
|
||||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||||
};
|
};
|
||||||
class AstDisable final : public AstNodeStmt {
|
class AstDisable final : public AstNodeStmt {
|
||||||
|
|
|
||||||
|
|
@ -724,6 +724,9 @@ AstNodeDType::CTypeRecursed AstNodeDType::cTypeRecurse(bool compound) const {
|
||||||
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
// + 1 below as VlQueue uses 0 to mean unlimited, 1 to mean size() max is 1
|
||||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||||
info.m_type += ">";
|
info.m_type += ">";
|
||||||
|
} else if (const auto* const adtypep = VN_CAST(dtypep, SampleQueueDType)) {
|
||||||
|
const CTypeRecursed sub = adtypep->subDTypep()->cTypeRecurse(true);
|
||||||
|
info.m_type = "VlSampleQueue<" + sub.m_type + ">";
|
||||||
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
} else if (const auto* const adtypep = VN_CAST(dtypep, ClassRefDType)) {
|
||||||
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
||||||
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
} else if (const auto* const adtypep = VN_CAST(dtypep, IfaceRefDType)) {
|
||||||
|
|
@ -2002,6 +2005,10 @@ bool AstWildcardArrayDType::similarDType(const AstNodeDType* samep) const {
|
||||||
return type() == samep->type() && asamep->subDTypep()
|
return type() == samep->type() && asamep->subDTypep()
|
||||||
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
&& subDTypep()->skipRefp()->similarDType(asamep->subDTypep()->skipRefp());
|
||||||
}
|
}
|
||||||
|
void AstSampleQueueDType::dumpSmall(std::ostream& str) const {
|
||||||
|
this->AstNodeDType::dumpSmall(str);
|
||||||
|
str << "[*]";
|
||||||
|
}
|
||||||
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
void AstUnsizedArrayDType::dumpSmall(std::ostream& str) const {
|
||||||
this->AstNodeDType::dumpSmall(str);
|
this->AstNodeDType::dumpSmall(str);
|
||||||
str << "[]";
|
str << "[]";
|
||||||
|
|
@ -2323,3 +2330,8 @@ AstAlways* AstAssignW::convertToAlways() {
|
||||||
replaceWith(newp); // User expected to then deleteTree();
|
replaceWith(newp); // User expected to then deleteTree();
|
||||||
return newp;
|
return newp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void AstDelay::dump(std::ostream& str) const {
|
||||||
|
this->AstNodeStmt::dump(str);
|
||||||
|
if (isCycleDelay()) str << " [CYCLE]";
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -70,7 +70,7 @@ private:
|
||||||
string m_namedScope; // Name of begin blocks above us
|
string m_namedScope; // Name of begin blocks above us
|
||||||
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
string m_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||||
int m_ifDepth = 0; // Current if depth
|
int m_ifDepth = 0; // Current if depth
|
||||||
bool m_underFork = false; // True if the current statement is directly under a fork
|
bool m_keepBegins = false; // True if begins should not be inlined
|
||||||
|
|
||||||
// METHODS
|
// METHODS
|
||||||
|
|
||||||
|
|
@ -123,11 +123,18 @@ private:
|
||||||
|
|
||||||
// VISITORS
|
// VISITORS
|
||||||
void visit(AstFork* nodep) override {
|
void visit(AstFork* nodep) override {
|
||||||
VL_RESTORER(m_underFork);
|
// Keep this begin to group its statements together
|
||||||
m_underFork = true;
|
VL_RESTORER(m_keepBegins);
|
||||||
|
m_keepBegins = true;
|
||||||
dotNames(nodep, "__FORK__");
|
dotNames(nodep, "__FORK__");
|
||||||
nodep->name("");
|
nodep->name("");
|
||||||
}
|
}
|
||||||
|
void visit(AstNodeAssign* nodep) override {
|
||||||
|
// Keep begin under assignment (in nodep->timingControlp())
|
||||||
|
VL_RESTORER(m_keepBegins);
|
||||||
|
m_keepBegins = true;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
void visit(AstNodeModule* nodep) override {
|
void visit(AstNodeModule* nodep) override {
|
||||||
VL_RESTORER(m_modp);
|
VL_RESTORER(m_modp);
|
||||||
{
|
{
|
||||||
|
|
@ -178,15 +185,14 @@ private:
|
||||||
VL_RESTORER(m_unnamedScope);
|
VL_RESTORER(m_unnamedScope);
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
VL_RESTORER(m_underFork);
|
VL_RESTORER(m_keepBegins);
|
||||||
m_underFork = false;
|
m_keepBegins = false;
|
||||||
dotNames(nodep, "__BEGIN__");
|
dotNames(nodep, "__BEGIN__");
|
||||||
}
|
}
|
||||||
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
if (m_underFork) {
|
if (m_keepBegins) {
|
||||||
// If we're under a fork, keep this begin to group its statements together
|
|
||||||
nodep->name("");
|
nodep->name("");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
@ -263,8 +269,8 @@ private:
|
||||||
}
|
}
|
||||||
// VISITORS - LINT CHECK
|
// VISITORS - LINT CHECK
|
||||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||||
VL_RESTORER(m_underFork);
|
VL_RESTORER(m_keepBegins);
|
||||||
m_underFork = false;
|
m_keepBegins = false;
|
||||||
// Check IFDEPTH warning - could be in other transform files if desire
|
// Check IFDEPTH warning - could be in other transform files if desire
|
||||||
VL_RESTORER(m_ifDepth);
|
VL_RESTORER(m_ifDepth);
|
||||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
||||||
|
|
@ -279,8 +285,8 @@ private:
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
void visit(AstNode* nodep) override {
|
void visit(AstNode* nodep) override {
|
||||||
VL_RESTORER(m_underFork);
|
VL_RESTORER(m_keepBegins);
|
||||||
m_underFork = false;
|
m_keepBegins = false;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -302,6 +302,11 @@ private:
|
||||||
}
|
}
|
||||||
|
|
||||||
//-----
|
//-----
|
||||||
|
void visit(AstClockingItem* nodep) override {
|
||||||
|
// Prevent V3Dead from deleting clockvars that are seemingly dead before V3AssertPre. Later
|
||||||
|
// the vars will be moved to the containing module so if they are actually dead they will
|
||||||
|
// still get deleted.
|
||||||
|
}
|
||||||
void visit(AstNode* nodep) override {
|
void visit(AstNode* nodep) override {
|
||||||
if (nodep->isOutputter()) m_sideEffect = true;
|
if (nodep->isOutputter()) m_sideEffect = true;
|
||||||
iterateChildren(nodep);
|
iterateChildren(nodep);
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ public:
|
||||||
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
||||||
&& !varp->isStatic() // Not a static variable
|
&& !varp->isStatic() // Not a static variable
|
||||||
&& !varp->isSc() // Aggregates can't be anon
|
&& !varp->isSc() // Aggregates can't be anon
|
||||||
|
&& !VN_IS(varp->dtypep()->skipRefp(), SampleQueueDType) // Aggregates can't be anon
|
||||||
&& (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon
|
&& (varp->basicp() && !varp->basicp()->isOpaque()); // Aggregates can't be anon
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -660,6 +660,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
||||||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||||
suffix + ".atDefault()" + cvtarray);
|
suffix + ".atDefault()" + cvtarray);
|
||||||
|
} else if (VN_IS(dtypep, SampleQueueDType)) {
|
||||||
|
return "";
|
||||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||||
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
||||||
"Should have swapped msb & lsb earlier.");
|
"Should have swapped msb & lsb earlier.");
|
||||||
|
|
|
||||||
|
|
@ -510,6 +510,7 @@ private:
|
||||||
void visit(AstCFunc* nodep) override {
|
void visit(AstCFunc* nodep) override {
|
||||||
iterateNewStmt(nodep, "User C Function", "User C Function");
|
iterateNewStmt(nodep, "User C Function", "User C Function");
|
||||||
}
|
}
|
||||||
|
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
|
||||||
void visit(AstSenItem* nodep) override {
|
void visit(AstSenItem* nodep) override {
|
||||||
m_inSenItem = true;
|
m_inSenItem = true;
|
||||||
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
||||||
|
|
|
||||||
|
|
@ -740,6 +740,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||||
// STATE
|
// STATE
|
||||||
LinkDotState* const m_statep; // State to pass between visitors, including symbol table
|
LinkDotState* const m_statep; // State to pass between visitors, including symbol table
|
||||||
AstNodeModule* m_classOrPackagep = nullptr; // Current package
|
AstNodeModule* m_classOrPackagep = nullptr; // Current package
|
||||||
|
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||||
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
|
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
|
||||||
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
|
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
|
||||||
string m_scope; // Scope text
|
string m_scope; // Scope text
|
||||||
|
|
@ -1123,6 +1124,49 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||||
m_ftaskp = nullptr;
|
m_ftaskp = nullptr;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstClocking* nodep) override {
|
||||||
|
VL_RESTORER(m_clockingp);
|
||||||
|
m_clockingp = nodep;
|
||||||
|
iterate(nodep->sensesp());
|
||||||
|
iterateAndNextNull(nodep->itemsp());
|
||||||
|
// If the block has no name, one cannot reference the clockvars
|
||||||
|
if (nodep->name().empty()) return;
|
||||||
|
VL_RESTORER(m_curSymp);
|
||||||
|
m_curSymp = m_statep->insertBlock(m_curSymp, nodep->name(), nodep, m_classOrPackagep);
|
||||||
|
m_curSymp->fallbackp(nullptr);
|
||||||
|
iterateAndNextNull(nodep->itemsp());
|
||||||
|
}
|
||||||
|
void visit(AstClockingItem* nodep) override {
|
||||||
|
if (nodep->varp()) {
|
||||||
|
if (m_curSymp->nodep() == m_clockingp) iterate(nodep->varp());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
std::string varname;
|
||||||
|
AstNodeDType* dtypep;
|
||||||
|
if (AstAssign* const assignp = nodep->assignp()) {
|
||||||
|
AstNodeExpr* const rhsp = assignp->rhsp()->unlinkFrBack();
|
||||||
|
dtypep = new AstRefDType{nodep->fileline(), AstRefDType::FlagTypeOfExpr{},
|
||||||
|
rhsp->cloneTree(false)};
|
||||||
|
nodep->exprp(rhsp);
|
||||||
|
varname = assignp->lhsp()->name();
|
||||||
|
VL_DO_DANGLING(assignp->unlinkFrBack()->deleteTree(), assignp);
|
||||||
|
} else {
|
||||||
|
AstNodeExpr* const refp = nodep->exprp();
|
||||||
|
const VSymEnt* foundp = m_curSymp->findIdFallback(refp->name());
|
||||||
|
if (!foundp || !foundp->nodep()) {
|
||||||
|
refp->v3error("Corresponding variable " << refp->prettyNameQ()
|
||||||
|
<< " does not exist");
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
varname = refp->name();
|
||||||
|
dtypep = VN_AS(foundp->nodep(), Var)->childDTypep()->cloneTree(false);
|
||||||
|
}
|
||||||
|
AstVar* const newvarp = new AstVar{nodep->fileline(), VVarType::MODULETEMP, varname,
|
||||||
|
VFlagChildDType{}, dtypep};
|
||||||
|
nodep->varp(newvarp);
|
||||||
|
iterate(nodep->exprp());
|
||||||
|
}
|
||||||
void visit(AstVar* nodep) override {
|
void visit(AstVar* nodep) override {
|
||||||
// Var: Remember its name for later resolution
|
// Var: Remember its name for later resolution
|
||||||
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?");
|
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?");
|
||||||
|
|
@ -1150,6 +1194,10 @@ class LinkDotFindVisitor final : public VNVisitor {
|
||||||
<< cvtToHex(foundp->parentp()) << endl);
|
<< cvtToHex(foundp->parentp()) << endl);
|
||||||
if (foundp->parentp() == m_curSymp // Only when on same level
|
if (foundp->parentp() == m_curSymp // Only when on same level
|
||||||
&& !foundp->imported()) { // and not from package
|
&& !foundp->imported()) { // and not from package
|
||||||
|
if (VN_IS(m_curSymp->nodep(), Clocking)) {
|
||||||
|
nodep->v3error("Multiple clockvars with the same name not allowed");
|
||||||
|
return;
|
||||||
|
}
|
||||||
const bool nansiBad
|
const bool nansiBad
|
||||||
= ((findvarp->isDeclTyped() && nodep->isDeclTyped())
|
= ((findvarp->isDeclTyped() && nodep->isDeclTyped())
|
||||||
|| (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output)
|
|| (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output)
|
||||||
|
|
@ -1946,6 +1994,7 @@ private:
|
||||||
AstNodeModule* m_modp = nullptr; // Current module
|
AstNodeModule* m_modp = nullptr; // Current module
|
||||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||||
int m_modportNum = 0; // Uniqueify modport numbers
|
int m_modportNum = 0; // Uniqueify modport numbers
|
||||||
|
bool m_inSens = false; // True if in senitem
|
||||||
|
|
||||||
struct DotStates {
|
struct DotStates {
|
||||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||||
|
|
@ -2062,6 +2111,24 @@ private:
|
||||||
refp->user5p(nodep);
|
refp->user5p(nodep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
VSymEnt* getCreateClockingEventSymEnt(AstClocking* clockingp) {
|
||||||
|
if (!clockingp->eventp()) {
|
||||||
|
AstVar* const eventp = new AstVar{
|
||||||
|
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
|
||||||
|
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
|
||||||
|
clockingp->eventp(eventp);
|
||||||
|
// Trigger the clocking event in Observed (IEEE 1800-2017 14.13)
|
||||||
|
clockingp->addNextHere(new AstAlwaysObserved{
|
||||||
|
clockingp->fileline(),
|
||||||
|
new AstSenTree{clockingp->fileline(), clockingp->sensesp()->cloneTree(false)},
|
||||||
|
new AstFireEvent{clockingp->fileline(),
|
||||||
|
new AstVarRef{clockingp->fileline(), eventp, VAccess::WRITE},
|
||||||
|
false}});
|
||||||
|
v3Global.setHasEvents();
|
||||||
|
eventp->user1p(new VSymEnt{m_statep->symsp(), eventp});
|
||||||
|
}
|
||||||
|
return reinterpret_cast<VSymEnt*>(clockingp->eventp()->user1p());
|
||||||
|
}
|
||||||
|
|
||||||
bool isParamedClassRef(const AstNode* nodep) {
|
bool isParamedClassRef(const AstNode* nodep) {
|
||||||
// Is this a parametrized reference to a class, or a reference to class parameter
|
// Is this a parametrized reference to a class, or a reference to class parameter
|
||||||
|
|
@ -2311,6 +2378,11 @@ private:
|
||||||
m_ds.m_dotp = lastStates.m_dotp;
|
m_ds.m_dotp = lastStates.m_dotp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstSenItem* nodep) override {
|
||||||
|
VL_RESTORER(m_inSens);
|
||||||
|
m_inSens = true;
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
void visit(AstParseRef* nodep) override {
|
void visit(AstParseRef* nodep) override {
|
||||||
if (nodep->user3SetOnce()) return;
|
if (nodep->user3SetOnce()) return;
|
||||||
UINFO(9, " linkPARSEREF " << m_ds.ascii() << " n=" << nodep << endl);
|
UINFO(9, " linkPARSEREF " << m_ds.ascii() << " n=" << nodep << endl);
|
||||||
|
|
@ -2417,6 +2489,12 @@ private:
|
||||||
}
|
}
|
||||||
// What fell out?
|
// What fell out?
|
||||||
bool ok = false;
|
bool ok = false;
|
||||||
|
// Special case: waiting on clocking event
|
||||||
|
if (m_inSens && foundp && m_ds.m_dotPos != DP_SCOPE) {
|
||||||
|
if (AstClocking* const clockingp = VN_CAST(foundp->nodep(), Clocking)) {
|
||||||
|
foundp = getCreateClockingEventSymEnt(clockingp);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (!foundp) {
|
if (!foundp) {
|
||||||
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), Begin)
|
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), Begin)
|
||||||
|| VN_IS(foundp->nodep(), Netlist) // for $root
|
|| VN_IS(foundp->nodep(), Netlist) // for $root
|
||||||
|
|
@ -2582,6 +2660,9 @@ private:
|
||||||
m_ds.m_dotPos = DP_MEMBER;
|
m_ds.m_dotPos = DP_MEMBER;
|
||||||
m_ds.m_dotText = "";
|
m_ds.m_dotText = "";
|
||||||
}
|
}
|
||||||
|
} else if (VN_IS(foundp->nodep(), Clocking)) {
|
||||||
|
m_ds.m_dotSymp = foundp;
|
||||||
|
ok = m_ds.m_dotPos == DP_SCOPE;
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
|
|
|
||||||
|
|
@ -60,6 +60,8 @@ private:
|
||||||
AstNodeModule* m_modp = nullptr; // Current module
|
AstNodeModule* m_modp = nullptr; // Current module
|
||||||
AstNodeFTask* m_ftaskp = nullptr; // Current task
|
AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||||
AstNodeDType* m_dtypep = nullptr; // Current data type
|
AstNodeDType* m_dtypep = nullptr; // Current data type
|
||||||
|
AstNodeExpr* m_defaultInSkewp = nullptr; // Current default input skew
|
||||||
|
AstNodeExpr* m_defaultOutSkewp = nullptr; // Current default output skew
|
||||||
int m_genblkAbove = 0; // Begin block number of if/case/for above
|
int m_genblkAbove = 0; // Begin block number of if/case/for above
|
||||||
int m_genblkNum = 0; // Begin block number, 0=none seen
|
int m_genblkNum = 0; // Begin block number, 0=none seen
|
||||||
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
||||||
|
|
@ -632,6 +634,66 @@ private:
|
||||||
nodep->classOrPackagep(m_stdPackagep);
|
nodep->classOrPackagep(m_stdPackagep);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstClocking* nodep) override {
|
||||||
|
VL_RESTORER(m_defaultInSkewp);
|
||||||
|
VL_RESTORER(m_defaultOutSkewp);
|
||||||
|
// Find default input and output skews
|
||||||
|
AstClockingItem* nextItemp = nodep->itemsp();
|
||||||
|
for (AstClockingItem* itemp = nextItemp; itemp; itemp = nextItemp) {
|
||||||
|
nextItemp = VN_AS(itemp->nextp(), ClockingItem);
|
||||||
|
if (itemp->exprp() || itemp->assignp()) continue;
|
||||||
|
if (itemp->skewp()) {
|
||||||
|
if (itemp->direction() == VDirection::INPUT) {
|
||||||
|
// Disallow default redefinition; note some simulators allow this
|
||||||
|
if (m_defaultInSkewp) {
|
||||||
|
itemp->skewp()->v3error("Multiple default input skews not allowed");
|
||||||
|
}
|
||||||
|
m_defaultInSkewp = itemp->skewp();
|
||||||
|
} else if (itemp->direction() == VDirection::OUTPUT) {
|
||||||
|
if (AstConst* const constp = VN_CAST(itemp->skewp(), Const)) {
|
||||||
|
if (constp->num().is1Step()) {
|
||||||
|
itemp->skewp()->v3error("1step not allowed as output skew");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Disallow default redefinition; note some simulators allow this
|
||||||
|
if (m_defaultOutSkewp) {
|
||||||
|
itemp->skewp()->v3error("Multiple default output skews not allowed");
|
||||||
|
}
|
||||||
|
m_defaultOutSkewp = itemp->skewp();
|
||||||
|
} else {
|
||||||
|
itemp->v3fatalSrc("Incorrect direction");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pushDeletep(itemp->unlinkFrBack());
|
||||||
|
}
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstClockingItem* nodep) override {
|
||||||
|
if (nodep->direction() == VDirection::OUTPUT) {
|
||||||
|
if (!nodep->skewp()) {
|
||||||
|
if (m_defaultOutSkewp) {
|
||||||
|
nodep->skewp(m_defaultOutSkewp->cloneTree(false));
|
||||||
|
} else {
|
||||||
|
// Default is 0 (IEEE 1800-2017 14.3)
|
||||||
|
nodep->skewp(new AstConst{nodep->fileline(), 0});
|
||||||
|
}
|
||||||
|
} else if (AstConst* const constp = VN_CAST(nodep->skewp(), Const)) {
|
||||||
|
if (constp->num().is1Step()) {
|
||||||
|
nodep->skewp()->v3error("1step not allowed as output skew");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (nodep->direction() == VDirection::INPUT) {
|
||||||
|
if (!nodep->skewp()) {
|
||||||
|
if (m_defaultInSkewp) {
|
||||||
|
nodep->skewp(m_defaultInSkewp->cloneTree(false));
|
||||||
|
} else {
|
||||||
|
// Default is 1step (IEEE 1800-2017 14.3)
|
||||||
|
nodep->skewp(new AstConst{nodep->fileline(), AstConst::OneStep{}});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
iterateChildren(nodep);
|
||||||
|
}
|
||||||
|
|
||||||
void visit(AstNode* nodep) override {
|
void visit(AstNode* nodep) override {
|
||||||
// Default: Just iterate
|
// Default: Just iterate
|
||||||
|
|
|
||||||
|
|
@ -494,6 +494,10 @@ V3Number& V3Number::setMask(int nbits) {
|
||||||
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
||||||
std::ostringstream out;
|
std::ostringstream out;
|
||||||
|
|
||||||
|
if (is1Step()) {
|
||||||
|
out << "1step";
|
||||||
|
return out.str();
|
||||||
|
}
|
||||||
if (isDouble()) {
|
if (isDouble()) {
|
||||||
out.precision(17);
|
out.precision(17);
|
||||||
if (VL_UNCOVERABLE(width() != 64)) {
|
if (VL_UNCOVERABLE(width() != 64)) {
|
||||||
|
|
|
||||||
|
|
@ -97,6 +97,7 @@ private:
|
||||||
public:
|
public:
|
||||||
bool m_sized : 1; // True if the user specified the width, else we track it.
|
bool m_sized : 1; // True if the user specified the width, else we track it.
|
||||||
bool m_signed : 1; // True if signed value
|
bool m_signed : 1; // True if signed value
|
||||||
|
bool m_is1Step : 1; // True if 1step
|
||||||
bool m_isNull : 1; // True if "null" versus normal 0
|
bool m_isNull : 1; // True if "null" versus normal 0
|
||||||
bool m_fromString : 1; // True if from string literal
|
bool m_fromString : 1; // True if from string literal
|
||||||
bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width
|
bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width
|
||||||
|
|
@ -107,6 +108,7 @@ public:
|
||||||
: m_type{V3NumberDataType::UNINITIALIZED}
|
: m_type{V3NumberDataType::UNINITIALIZED}
|
||||||
, m_sized{false}
|
, m_sized{false}
|
||||||
, m_signed{false}
|
, m_signed{false}
|
||||||
|
, m_is1Step{false}
|
||||||
, m_isNull{false}
|
, m_isNull{false}
|
||||||
, m_fromString{false}
|
, m_fromString{false}
|
||||||
, m_autoExtend{false} {}
|
, m_autoExtend{false} {}
|
||||||
|
|
@ -118,6 +120,7 @@ public:
|
||||||
, m_type{other.m_type}
|
, m_type{other.m_type}
|
||||||
, m_sized{other.m_sized}
|
, m_sized{other.m_sized}
|
||||||
, m_signed{other.m_signed}
|
, m_signed{other.m_signed}
|
||||||
|
, m_is1Step{other.m_is1Step}
|
||||||
, m_isNull{other.m_isNull}
|
, m_isNull{other.m_isNull}
|
||||||
, m_fromString{other.m_fromString}
|
, m_fromString{other.m_fromString}
|
||||||
, m_autoExtend{other.m_autoExtend} {
|
, m_autoExtend{other.m_autoExtend} {
|
||||||
|
|
@ -145,6 +148,7 @@ public:
|
||||||
m_type = other.m_type;
|
m_type = other.m_type;
|
||||||
m_sized = other.m_sized;
|
m_sized = other.m_sized;
|
||||||
m_signed = other.m_signed;
|
m_signed = other.m_signed;
|
||||||
|
m_is1Step = other.m_is1Step;
|
||||||
m_isNull = other.m_isNull;
|
m_isNull = other.m_isNull;
|
||||||
m_fromString = other.m_fromString;
|
m_fromString = other.m_fromString;
|
||||||
m_autoExtend = other.m_autoExtend;
|
m_autoExtend = other.m_autoExtend;
|
||||||
|
|
@ -156,6 +160,7 @@ public:
|
||||||
, m_type{other.m_type}
|
, m_type{other.m_type}
|
||||||
, m_sized{other.m_sized}
|
, m_sized{other.m_sized}
|
||||||
, m_signed{other.m_signed}
|
, m_signed{other.m_signed}
|
||||||
|
, m_is1Step{other.m_is1Step}
|
||||||
, m_isNull{other.m_isNull}
|
, m_isNull{other.m_isNull}
|
||||||
, m_fromString{other.m_fromString}
|
, m_fromString{other.m_fromString}
|
||||||
, m_autoExtend{other.m_autoExtend} {
|
, m_autoExtend{other.m_autoExtend} {
|
||||||
|
|
@ -184,6 +189,7 @@ public:
|
||||||
m_type = other.m_type;
|
m_type = other.m_type;
|
||||||
m_sized = other.m_sized;
|
m_sized = other.m_sized;
|
||||||
m_signed = other.m_signed;
|
m_signed = other.m_signed;
|
||||||
|
m_is1Step = other.m_is1Step;
|
||||||
m_isNull = other.m_isNull;
|
m_isNull = other.m_isNull;
|
||||||
m_fromString = other.m_fromString;
|
m_fromString = other.m_fromString;
|
||||||
m_autoExtend = other.m_autoExtend;
|
m_autoExtend = other.m_autoExtend;
|
||||||
|
|
@ -489,6 +495,11 @@ public:
|
||||||
setString(value);
|
setString(value);
|
||||||
m_data.m_fromString = true;
|
m_data.m_fromString = true;
|
||||||
}
|
}
|
||||||
|
class OneStep {};
|
||||||
|
V3Number(OneStep, AstNode* nodep) {
|
||||||
|
init(nodep, 64);
|
||||||
|
m_data.m_is1Step = true;
|
||||||
|
}
|
||||||
class Null {};
|
class Null {};
|
||||||
V3Number(Null, AstNode* nodep) {
|
V3Number(Null, AstNode* nodep) {
|
||||||
init(nodep);
|
init(nodep);
|
||||||
|
|
@ -601,6 +612,7 @@ public:
|
||||||
|| m_data.type() == V3NumberDataType::DOUBLE;
|
|| m_data.type() == V3NumberDataType::DOUBLE;
|
||||||
}
|
}
|
||||||
bool isNegative() const VL_MT_SAFE { return !isString() && bitIs1(width() - 1); }
|
bool isNegative() const VL_MT_SAFE { return !isString() && bitIs1(width() - 1); }
|
||||||
|
bool is1Step() const VL_MT_SAFE { return m_data.m_is1Step; }
|
||||||
bool isNull() const VL_MT_SAFE { return m_data.m_isNull; }
|
bool isNull() const VL_MT_SAFE { return m_data.m_isNull; }
|
||||||
bool isFourState() const VL_MT_SAFE;
|
bool isFourState() const VL_MT_SAFE;
|
||||||
bool hasZ() const {
|
bool hasZ() const {
|
||||||
|
|
|
||||||
|
|
@ -385,6 +385,12 @@ class OrderBuildVisitor final : public VNVisitor {
|
||||||
iterateLogic(nodep);
|
iterateLogic(nodep);
|
||||||
m_inPost = false;
|
m_inPost = false;
|
||||||
}
|
}
|
||||||
|
void visit(AstAlwaysObserved* nodep) override { //
|
||||||
|
iterateLogic(nodep);
|
||||||
|
}
|
||||||
|
void visit(AstAlwaysReactive* nodep) override { //
|
||||||
|
iterateLogic(nodep);
|
||||||
|
}
|
||||||
void visit(AstFinal* nodep) override { // LCOV_EXCL_START
|
void visit(AstFinal* nodep) override { // LCOV_EXCL_START
|
||||||
nodep->v3fatalSrc("AstFinal should not need ordering");
|
nodep->v3fatalSrc("AstFinal should not need ordering");
|
||||||
} // LCOV_EXCL_STOP
|
} // LCOV_EXCL_STOP
|
||||||
|
|
|
||||||
274
src/V3Sched.cpp
274
src/V3Sched.cpp
|
|
@ -192,7 +192,13 @@ LogicClasses gatherLogicClasses(AstNetlist* netlistp) {
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
UASSERT_OBJ(senTreep->hasClocked(), activep, "What else could it be?");
|
UASSERT_OBJ(senTreep->hasClocked(), activep, "What else could it be?");
|
||||||
result.m_clocked.emplace_back(scopep, activep);
|
if (VN_IS(activep->stmtsp(), AlwaysObserved)) {
|
||||||
|
result.m_observed.emplace_back(scopep, activep);
|
||||||
|
} else if (VN_IS(activep->stmtsp(), AlwaysReactive)) {
|
||||||
|
result.m_reactive.emplace_back(scopep, activep);
|
||||||
|
} else {
|
||||||
|
result.m_clocked.emplace_back(scopep, activep);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
@ -332,6 +338,20 @@ struct TriggerKit {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// EvalKit groups items that have to be passed to createEval() for a given eval region
|
||||||
|
|
||||||
|
struct EvalKit {
|
||||||
|
// The TRIGGERVEC AstVarScope representing the region's trigger flags
|
||||||
|
AstVarScope* const m_vscp = nullptr;
|
||||||
|
// The AstCFunc that computes the region's active triggers
|
||||||
|
AstCFunc* const m_triggerComputep = nullptr;
|
||||||
|
// The AstCFunc that dumps the region's active triggers
|
||||||
|
AstCFunc* const m_dumpp = nullptr;
|
||||||
|
// The AstCFunc that evaluates the region's logic
|
||||||
|
AstCFunc* const m_funcp = nullptr;
|
||||||
|
};
|
||||||
|
|
||||||
// Create an AstSenTree that is sensitive to the given trigger index. Must not exist yet!
|
// Create an AstSenTree that is sensitive to the given trigger index. Must not exist yet!
|
||||||
AstSenTree* createTriggerSenTree(AstNetlist* netlistp, AstVarScope* const vscp, uint32_t index) {
|
AstSenTree* createTriggerSenTree(AstNetlist* netlistp, AstVarScope* const vscp, uint32_t index) {
|
||||||
AstTopScope* const topScopep = netlistp->topScopep();
|
AstTopScope* const topScopep = netlistp->topScopep();
|
||||||
|
|
@ -765,15 +785,45 @@ AstNode* createInputCombLoop(AstNetlist* netlistp, AstCFunc* const initFuncp,
|
||||||
}
|
}
|
||||||
|
|
||||||
//============================================================================
|
//============================================================================
|
||||||
// Bold together parts to create the top level _eval function
|
// Helpers for 'createEval'
|
||||||
|
|
||||||
|
AstStmtExpr* createTriggerClearCall(FileLine* const flp, AstVarScope* const vscp) { // Trigger
|
||||||
|
AstVarRef* const refp = new AstVarRef{flp, vscp, VAccess::WRITE};
|
||||||
|
AstCMethodHard* const callp = new AstCMethodHard{flp, refp, "clear"};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
return callp->makeStmt();
|
||||||
|
}
|
||||||
|
|
||||||
|
AstStmtExpr* createTriggerSetCall(FileLine* const flp, AstVarScope* const toVscp,
|
||||||
|
AstVarScope* const fromVscp) {
|
||||||
|
AstVarRef* const lhsp = new AstVarRef{flp, toVscp, VAccess::WRITE};
|
||||||
|
AstVarRef* const argp = new AstVarRef{flp, fromVscp, VAccess::READ};
|
||||||
|
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "set", argp};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
return callp->makeStmt();
|
||||||
|
}
|
||||||
|
|
||||||
|
AstStmtExpr* createTriggerAndNotCall(FileLine* const flp, AstVarScope* const lhsVscp,
|
||||||
|
AstVarScope* const aVscp, AstVarScope* const bVscp) {
|
||||||
|
AstVarRef* const lhsp = new AstVarRef{flp, lhsVscp, VAccess::WRITE};
|
||||||
|
AstVarRef* const opap = new AstVarRef{flp, aVscp, VAccess::READ};
|
||||||
|
AstVarRef* const opbp = new AstVarRef{flp, bVscp, VAccess::READ};
|
||||||
|
opap->addNext(opbp);
|
||||||
|
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "andNot", opap};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
return callp->makeStmt();
|
||||||
|
}
|
||||||
|
|
||||||
|
//============================================================================
|
||||||
|
// Bolt together parts to create the top level _eval function
|
||||||
|
|
||||||
void createEval(AstNetlist* netlistp, //
|
void createEval(AstNetlist* netlistp, //
|
||||||
AstNode* icoLoop, //
|
AstNode* icoLoop, //
|
||||||
const TriggerKit& actTrig, //
|
const EvalKit& actKit, //
|
||||||
AstVarScope* preTrigsp, //
|
AstVarScope* preTrigsp, //
|
||||||
AstVarScope* nbaTrigsp, //
|
const EvalKit& nbaKit, //
|
||||||
AstCFunc* actFuncp, //
|
const EvalKit& obsKit, //
|
||||||
AstCFunc* nbaFuncp, //
|
const EvalKit& reactKit, //
|
||||||
AstCFunc* postponedFuncp, //
|
AstCFunc* postponedFuncp, //
|
||||||
TimingKit& timingKit //
|
TimingKit& timingKit //
|
||||||
) {
|
) {
|
||||||
|
|
@ -785,31 +835,16 @@ void createEval(AstNetlist* netlistp, //
|
||||||
// Start with the ico loop, if any
|
// Start with the ico loop, if any
|
||||||
if (icoLoop) funcp->addStmtsp(icoLoop);
|
if (icoLoop) funcp->addStmtsp(icoLoop);
|
||||||
|
|
||||||
// Create the NBA trigger dumping function, which is the same as act trigger
|
|
||||||
// dumping function, but referencing the nba trigger vector.
|
|
||||||
AstCFunc* const nbaDumpp = actTrig.m_dumpp->cloneTree(false);
|
|
||||||
actTrig.m_dumpp->addNextHere(nbaDumpp);
|
|
||||||
nbaDumpp->name("_dump_triggers__nba");
|
|
||||||
nbaDumpp->foreach([&](AstVarRef* refp) {
|
|
||||||
UASSERT_OBJ(refp->access().isReadOnly(), refp, "Should only read state");
|
|
||||||
if (refp->varScopep() == actTrig.m_vscp) {
|
|
||||||
refp->replaceWith(new AstVarRef{refp->fileline(), nbaTrigsp, VAccess::READ});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
nbaDumpp->foreach([&](AstText* textp) { //
|
|
||||||
textp->text(VString::replaceWord(textp->text(), "act", "nba"));
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create the active eval loop
|
// Create the active eval loop
|
||||||
AstNodeStmt* const activeEvalLoopp
|
AstNodeStmt* const activeEvalLoopp
|
||||||
= makeEvalLoop(
|
= makeEvalLoop(
|
||||||
netlistp, "act", "Active", actTrig.m_vscp, actTrig.m_dumpp,
|
netlistp, "act", "Active", actKit.m_vscp, actKit.m_dumpp,
|
||||||
[&]() { // Trigger
|
[&]() { // Trigger
|
||||||
AstNodeStmt* resultp = nullptr;
|
AstNodeStmt* resultp = nullptr;
|
||||||
|
|
||||||
// Compute the current triggers
|
// Compute the current triggers
|
||||||
{
|
{
|
||||||
AstCCall* const trigsp = new AstCCall{flp, actTrig.m_funcp};
|
AstCCall* const trigsp = new AstCCall{flp, actKit.m_triggerComputep};
|
||||||
trigsp->dtypeSetVoid();
|
trigsp->dtypeSetVoid();
|
||||||
resultp = AstNode::addNext(resultp, trigsp->makeStmt());
|
resultp = AstNode::addNext(resultp, trigsp->makeStmt());
|
||||||
}
|
}
|
||||||
|
|
@ -822,38 +857,21 @@ void createEval(AstNetlist* netlistp, //
|
||||||
return resultp;
|
return resultp;
|
||||||
},
|
},
|
||||||
[&]() { // Body
|
[&]() { // Body
|
||||||
AstNodeStmt* resultp = nullptr;
|
|
||||||
|
|
||||||
// Compute the pre triggers
|
// Compute the pre triggers
|
||||||
{
|
AstNodeStmt* resultp
|
||||||
AstVarRef* const lhsp = new AstVarRef{flp, preTrigsp, VAccess::WRITE};
|
= createTriggerAndNotCall(flp, preTrigsp, actKit.m_vscp, nbaKit.m_vscp);
|
||||||
AstVarRef* const opap = new AstVarRef{flp, actTrig.m_vscp, VAccess::READ};
|
|
||||||
AstVarRef* const opbp = new AstVarRef{flp, nbaTrigsp, VAccess::READ};
|
|
||||||
opap->addNext(opbp);
|
|
||||||
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "andNot", opap};
|
|
||||||
callp->dtypeSetVoid();
|
|
||||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Latch the active trigger flags under the NBA trigger flags
|
// Latch the active trigger flags under the NBA trigger flags
|
||||||
{
|
resultp = AstNode::addNext(
|
||||||
AstVarRef* const lhsp = new AstVarRef{flp, nbaTrigsp, VAccess::WRITE};
|
resultp, createTriggerSetCall(flp, nbaKit.m_vscp, actKit.m_vscp));
|
||||||
AstVarRef* const argp = new AstVarRef{flp, actTrig.m_vscp, VAccess::READ};
|
|
||||||
AstCMethodHard* const callp = new AstCMethodHard{flp, lhsp, "set", argp};
|
|
||||||
callp->dtypeSetVoid();
|
|
||||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Resume triggered timing schedulers
|
// Resume triggered timing schedulers
|
||||||
if (AstCCall* const resumep = timingKit.createResume(netlistp)) {
|
if (AstCCall* const resumep = timingKit.createResume(netlistp)) {
|
||||||
resultp = AstNode::addNext(resultp, resumep->makeStmt());
|
resultp = AstNode::addNext(resultp, resumep->makeStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
// Invoke body function
|
// Invoke body function
|
||||||
{
|
{
|
||||||
AstCCall* const callp = new AstCCall{flp, actFuncp};
|
AstCCall* const callp = new AstCCall{flp, actKit.m_funcp};
|
||||||
callp->dtypeSetVoid();
|
callp->dtypeSetVoid();
|
||||||
return AstNode::addNext(resultp, callp->makeStmt());
|
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
||||||
}
|
}
|
||||||
|
|
||||||
return resultp;
|
return resultp;
|
||||||
|
|
@ -861,32 +879,75 @@ void createEval(AstNetlist* netlistp, //
|
||||||
.second;
|
.second;
|
||||||
|
|
||||||
// Create the NBA eval loop. This uses the Active eval loop in the trigger section.
|
// Create the NBA eval loop. This uses the Active eval loop in the trigger section.
|
||||||
AstNodeStmt* const nbaEvalLoopp
|
AstNodeStmt* topEvalLoopp
|
||||||
= makeEvalLoop(
|
= makeEvalLoop(
|
||||||
netlistp, "nba", "NBA", nbaTrigsp, nbaDumpp,
|
netlistp, "nba", "NBA", nbaKit.m_vscp, nbaKit.m_dumpp,
|
||||||
[&]() { // Trigger
|
[&]() { // Trigger
|
||||||
AstNodeStmt* resultp = nullptr;
|
|
||||||
|
|
||||||
// Reset NBA triggers
|
// Reset NBA triggers
|
||||||
{
|
AstNodeStmt* resultp = createTriggerClearCall(flp, nbaKit.m_vscp);
|
||||||
AstVarRef* const refp = new AstVarRef{flp, nbaTrigsp, VAccess::WRITE};
|
|
||||||
AstCMethodHard* const callp = new AstCMethodHard{flp, refp, "clear"};
|
|
||||||
callp->dtypeSetVoid();
|
|
||||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the Active eval loop
|
// Run the Active eval loop
|
||||||
return AstNode::addNext(resultp, activeEvalLoopp);
|
resultp = AstNode::addNext(resultp, activeEvalLoopp);
|
||||||
|
return resultp;
|
||||||
},
|
},
|
||||||
[&]() { // Body
|
[&]() { // Body
|
||||||
AstCCall* const callp = new AstCCall{flp, nbaFuncp};
|
AstCCall* const callp = new AstCCall{flp, nbaKit.m_funcp};
|
||||||
callp->dtypeSetVoid();
|
callp->dtypeSetVoid();
|
||||||
return callp->makeStmt();
|
AstNodeStmt* resultp = callp->makeStmt();
|
||||||
|
// Latch the NBA trigger flags under the following region's trigger flags
|
||||||
|
AstVarScope* const nextVscp = obsKit.m_vscp ? obsKit.m_vscp : reactKit.m_vscp;
|
||||||
|
if (nextVscp) {
|
||||||
|
resultp = AstNode::addNext(
|
||||||
|
resultp, createTriggerSetCall(flp, nextVscp, nbaKit.m_vscp));
|
||||||
|
}
|
||||||
|
return resultp;
|
||||||
})
|
})
|
||||||
.second;
|
.second;
|
||||||
|
|
||||||
// Add the NBA eval loop
|
if (obsKit.m_funcp) {
|
||||||
funcp->addStmtsp(nbaEvalLoopp);
|
// Create the Observed eval loop. This uses the NBA eval loop in the trigger section.
|
||||||
|
topEvalLoopp
|
||||||
|
= makeEvalLoop(
|
||||||
|
netlistp, "obs", "Observed", obsKit.m_vscp, obsKit.m_dumpp,
|
||||||
|
[&]() { // Trigger
|
||||||
|
// Reset Observed triggers
|
||||||
|
AstNodeStmt* resultp = createTriggerClearCall(flp, obsKit.m_vscp);
|
||||||
|
// Run the NBA eval loop
|
||||||
|
resultp = AstNode::addNext(resultp, topEvalLoopp);
|
||||||
|
return resultp;
|
||||||
|
},
|
||||||
|
[&]() { // Body
|
||||||
|
AstCCall* const callp = new AstCCall{flp, obsKit.m_funcp};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
AstNodeStmt* resultp = callp->makeStmt();
|
||||||
|
// Latch the Observed trigger flags under the Reactive trigger flags
|
||||||
|
if (reactKit.m_vscp) {
|
||||||
|
resultp = AstNode::addNext(
|
||||||
|
resultp, createTriggerSetCall(flp, reactKit.m_vscp, obsKit.m_vscp));
|
||||||
|
}
|
||||||
|
return resultp;
|
||||||
|
})
|
||||||
|
.second;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reactKit.m_funcp) {
|
||||||
|
// Create the Reactive eval loop. This uses the previous eval loop in the trigger section.
|
||||||
|
topEvalLoopp = makeEvalLoop(
|
||||||
|
netlistp, "react", "Reactive", reactKit.m_vscp, reactKit.m_dumpp,
|
||||||
|
[&]() { // Trigger
|
||||||
|
// Reset Reactive triggers
|
||||||
|
AstNodeStmt* resultp = createTriggerClearCall(flp, reactKit.m_vscp);
|
||||||
|
// Run the previous eval loop
|
||||||
|
resultp = AstNode::addNext(resultp, topEvalLoopp);
|
||||||
|
return resultp;
|
||||||
|
},
|
||||||
|
[&]() { // Body
|
||||||
|
auto* const callp = new AstCCall{flp, reactKit.m_funcp};
|
||||||
|
callp->dtypeSetVoid();
|
||||||
|
return callp->makeStmt();
|
||||||
|
})
|
||||||
|
.second;
|
||||||
|
}
|
||||||
|
funcp->addStmtsp(topEvalLoopp);
|
||||||
|
|
||||||
// Add the Postponed eval call
|
// Add the Postponed eval call
|
||||||
if (postponedFuncp) {
|
if (postponedFuncp) {
|
||||||
|
|
@ -989,6 +1050,8 @@ void schedule(AstNetlist* netlistp) {
|
||||||
const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
|
const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
|
||||||
&logicRegions.m_act, //
|
&logicRegions.m_act, //
|
||||||
&logicRegions.m_nba, //
|
&logicRegions.m_nba, //
|
||||||
|
&logicClasses.m_observed, //
|
||||||
|
&logicClasses.m_reactive, //
|
||||||
&timingKit.m_lbs});
|
&timingKit.m_lbs});
|
||||||
const TriggerKit& actTrig
|
const TriggerKit& actTrig
|
||||||
= createTriggers(netlistp, initp, senExprBuilder, senTreeps, "act", extraTriggers);
|
= createTriggers(netlistp, initp, senExprBuilder, senTreeps, "act", extraTriggers);
|
||||||
|
|
@ -1002,7 +1065,6 @@ void schedule(AstNetlist* netlistp) {
|
||||||
|
|
||||||
AstVarScope* const actTrigVscp = actTrig.m_vscp;
|
AstVarScope* const actTrigVscp = actTrig.m_vscp;
|
||||||
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
||||||
AstVarScope* const nbaTrigVscp = scopeTopp->createTempLike("__VnbaTriggered", actTrigVscp);
|
|
||||||
|
|
||||||
const auto cloneMapWithNewTriggerReferences
|
const auto cloneMapWithNewTriggerReferences
|
||||||
= [=](std::unordered_map<const AstSenTree*, AstSenTree*> map, AstVarScope* vscp) {
|
= [=](std::unordered_map<const AstSenTree*, AstSenTree*> map, AstVarScope* vscp) {
|
||||||
|
|
@ -1025,7 +1087,6 @@ void schedule(AstNetlist* netlistp) {
|
||||||
|
|
||||||
const auto& actTrigMap = actTrig.m_map;
|
const auto& actTrigMap = actTrig.m_map;
|
||||||
const auto preTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, preTrigVscp);
|
const auto preTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, preTrigVscp);
|
||||||
const auto nbaTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, nbaTrigVscp);
|
|
||||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-triggers");
|
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-triggers");
|
||||||
|
|
||||||
// Note: Experiments so far show that running the Act (or Ico) regions on
|
// Note: Experiments so far show that running the Act (or Ico) regions on
|
||||||
|
|
@ -1061,37 +1122,78 @@ void schedule(AstNetlist* netlistp) {
|
||||||
splitCheck(actFuncp);
|
splitCheck(actFuncp);
|
||||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
|
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-act");
|
||||||
|
|
||||||
// Step 10: Create the 'nba' region evaluation function
|
const EvalKit& actKit = {actTrig.m_vscp, actTrig.m_funcp, actTrig.m_dumpp, actFuncp};
|
||||||
|
|
||||||
// Remap sensitivities of the input logic to the triggers
|
// Orders a region's logic and creates the region eval function
|
||||||
remapSensitivities(logicRegions.m_nba, nbaTrigMap);
|
const auto order = [&](const std::string& name,
|
||||||
remapSensitivities(logicReplicas.m_nba, nbaTrigMap);
|
const std::vector<V3Sched::LogicByScope*>& logic) -> EvalKit {
|
||||||
const auto& nbaTimingDomains = timingKit.remapDomains(nbaTrigMap);
|
AstVarScope* const trigVscp
|
||||||
|
= scopeTopp->createTempLike("__V" + name + "Triggered", actTrigVscp);
|
||||||
|
const auto trigMap = cloneMapWithNewTriggerReferences(actTrigMap, trigVscp);
|
||||||
|
// Remap sensitivities of the input logic to the triggers
|
||||||
|
for (LogicByScope* lbs : logic) remapSensitivities(*lbs, trigMap);
|
||||||
|
|
||||||
// Create the inverse map from trigger ref AstSenTree to original AstSenTree
|
// Create the inverse map from trigger ref AstSenTree to original AstSenTree
|
||||||
std::unordered_map<const AstSenItem*, const AstSenTree*> trigToSenNba;
|
std::unordered_map<const AstSenItem*, const AstSenTree*> trigToSen;
|
||||||
invertAndMergeSenTreeMap(trigToSenNba, nbaTrigMap);
|
invertAndMergeSenTreeMap(trigToSen, trigMap);
|
||||||
|
|
||||||
AstSenTree* const dpiExportTriggeredNba
|
AstSenTree* const dpiExportTriggered
|
||||||
= createTriggerSenTree(netlistp, nbaTrigVscp, dpiExportTriggerIndex);
|
= createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex);
|
||||||
|
|
||||||
AstCFunc* const nbaFuncp = V3Order::order(
|
const auto& timingDomains = timingKit.remapDomains(trigMap);
|
||||||
netlistp, {&logicRegions.m_nba, &logicReplicas.m_nba}, trigToSenNba, "nba",
|
AstCFunc* const funcp = V3Order::order(
|
||||||
v3Global.opt.mtasks(), false, [&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
|
netlistp, logic, trigToSen, name, name == "nba" && v3Global.opt.mtasks(), false,
|
||||||
auto it = nbaTimingDomains.find(vscp);
|
[&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
|
||||||
if (it != nbaTimingDomains.end()) out = it->second;
|
auto it = timingDomains.find(vscp);
|
||||||
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredNba);
|
if (it != timingDomains.end()) out = it->second;
|
||||||
|
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggered);
|
||||||
|
});
|
||||||
|
|
||||||
|
// Create the trigger dumping function, which is the same as act trigger
|
||||||
|
// dumping function, but referencing this region's trigger vector.
|
||||||
|
AstCFunc* const dumpp = actTrig.m_dumpp->cloneTree(false);
|
||||||
|
actTrig.m_dumpp->addNextHere(dumpp);
|
||||||
|
dumpp->name("_dump_triggers__" + name);
|
||||||
|
dumpp->foreach([&](AstVarRef* refp) {
|
||||||
|
UASSERT_OBJ(refp->access().isReadOnly(), refp, "Should only read state");
|
||||||
|
if (refp->varScopep() == actTrig.m_vscp) {
|
||||||
|
refp->replaceWith(new AstVarRef{refp->fileline(), trigVscp, VAccess::READ});
|
||||||
|
}
|
||||||
});
|
});
|
||||||
splitCheck(nbaFuncp);
|
dumpp->foreach([&](AstText* textp) { //
|
||||||
netlistp->evalNbap(nbaFuncp); // Remember for V3LifePost
|
textp->text(VString::replaceWord(textp->text(), "act", name));
|
||||||
|
});
|
||||||
|
|
||||||
|
return {trigVscp, nullptr, dumpp, funcp};
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 10: Create the 'nba' region evaluation function
|
||||||
|
const EvalKit& nbaKit = order("nba", {&logicRegions.m_nba, &logicReplicas.m_nba});
|
||||||
|
splitCheck(nbaKit.m_funcp);
|
||||||
|
netlistp->evalNbap(nbaKit.m_funcp); // Remember for V3LifePost
|
||||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-nba");
|
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-nba");
|
||||||
|
|
||||||
// Step 11: Create the 'postponed' region evaluation function
|
// Orders a region's logic and creates the region eval function (only if there is any logic in
|
||||||
|
// the region)
|
||||||
|
const auto orderIfNonEmpty = [&](const std::string& name, LogicByScope& lbs) -> EvalKit {
|
||||||
|
if (lbs.empty()) return {};
|
||||||
|
const auto& kit = order(name, {&lbs});
|
||||||
|
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-" + name);
|
||||||
|
return kit;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Step 11: Create the 'obs' region evaluation function
|
||||||
|
const EvalKit& obsKit = orderIfNonEmpty("obs", logicClasses.m_observed);
|
||||||
|
|
||||||
|
// Step 12: Create the 're' region evaluation function
|
||||||
|
const EvalKit& reactKit = orderIfNonEmpty("react", logicClasses.m_reactive);
|
||||||
|
|
||||||
|
// Step 13: Create the 'postponed' region evaluation function
|
||||||
auto* const postponedFuncp = createPostponed(netlistp, logicClasses);
|
auto* const postponedFuncp = createPostponed(netlistp, logicClasses);
|
||||||
|
|
||||||
// Step 12: Bolt it all together to create the '_eval' function
|
// Step 14: Bolt it all together to create the '_eval' function
|
||||||
createEval(netlistp, icoLoopp, actTrig, preTrigVscp, nbaTrigVscp, actFuncp, nbaFuncp,
|
createEval(netlistp, icoLoopp, actKit, preTrigVscp, nbaKit, obsKit, reactKit, postponedFuncp,
|
||||||
postponedFuncp, timingKit);
|
timingKit);
|
||||||
|
|
||||||
transformForks(netlistp);
|
transformForks(netlistp);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -82,6 +82,8 @@ struct LogicClasses final {
|
||||||
LogicByScope m_clocked; // Clocked (or sequential) logic (logic with explicit sensitivities)
|
LogicByScope m_clocked; // Clocked (or sequential) logic (logic with explicit sensitivities)
|
||||||
LogicByScope m_hybrid; // Hybrid logic (combinational logic with some explicit sensitivities)
|
LogicByScope m_hybrid; // Hybrid logic (combinational logic with some explicit sensitivities)
|
||||||
LogicByScope m_postponed; // Postponed logic ($strobe)
|
LogicByScope m_postponed; // Postponed logic ($strobe)
|
||||||
|
LogicByScope m_observed; // Observed logic (contains AstAlwaysObserved)
|
||||||
|
LogicByScope m_reactive; // Reactive logic (contains AstAlwaysReactive)
|
||||||
|
|
||||||
LogicClasses() = default;
|
LogicClasses() = default;
|
||||||
VL_UNCOPYABLE(LogicClasses);
|
VL_UNCOPYABLE(LogicClasses);
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,7 @@ private:
|
||||||
// STATE, for passing down one level of hierarchy (may need save/restore)
|
// STATE, for passing down one level of hierarchy (may need save/restore)
|
||||||
AstCell* m_aboveCellp = nullptr; // Cell that instantiates this module
|
AstCell* m_aboveCellp = nullptr; // Cell that instantiates this module
|
||||||
AstScope* m_aboveScopep = nullptr; // Scope that instantiates this scope
|
AstScope* m_aboveScopep = nullptr; // Scope that instantiates this scope
|
||||||
|
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||||
|
|
||||||
std::unordered_map<AstNodeModule*, AstScope*> m_packageScopes; // Scopes for each package
|
std::unordered_map<AstNodeModule*, AstScope*> m_packageScopes; // Scopes for each package
|
||||||
VarScopeMap m_varScopes; // Varscopes created for each scope and var
|
VarScopeMap m_varScopes; // Varscopes created for each scope and var
|
||||||
|
|
@ -247,6 +248,15 @@ private:
|
||||||
// We iterate under the *clone*
|
// We iterate under the *clone*
|
||||||
iterateChildren(clonep);
|
iterateChildren(clonep);
|
||||||
}
|
}
|
||||||
|
void visit(AstClocking* nodep) override {
|
||||||
|
VL_RESTORER(m_clockingp);
|
||||||
|
m_clockingp = nodep;
|
||||||
|
UINFO(4, " CLOCKING " << nodep << endl);
|
||||||
|
iterateChildren(nodep);
|
||||||
|
if (nodep->varsp()) m_scopep->modp()->addStmtsp(nodep->varsp()->unlinkFrBackWithNext());
|
||||||
|
if (nodep->eventp()) m_scopep->modp()->addStmtsp(nodep->eventp()->unlinkFrBack());
|
||||||
|
VL_DO_DANGLING(nodep->unlinkFrBack()->deleteTree(), nodep);
|
||||||
|
}
|
||||||
void visit(AstNodeFTask* nodep) override {
|
void visit(AstNodeFTask* nodep) override {
|
||||||
// Add to list of blocks under this scope
|
// Add to list of blocks under this scope
|
||||||
UINFO(4, " FTASK " << nodep << endl);
|
UINFO(4, " FTASK " << nodep << endl);
|
||||||
|
|
@ -265,7 +275,9 @@ private:
|
||||||
}
|
}
|
||||||
void visit(AstVar* nodep) override {
|
void visit(AstVar* nodep) override {
|
||||||
// Make new scope variable
|
// Make new scope variable
|
||||||
if (!nodep->user1p()) {
|
if (m_clockingp) {
|
||||||
|
nodep->name(VString::dot(m_clockingp->name(), "__DOT__", nodep->name()));
|
||||||
|
} else if (!nodep->user1p()) {
|
||||||
AstScope* scopep = m_scopep;
|
AstScope* scopep = m_scopep;
|
||||||
if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
||||||
// Attach every non-virtual interface variable its inner scope
|
// Attach every non-virtual interface variable its inner scope
|
||||||
|
|
|
||||||
|
|
@ -155,8 +155,8 @@ private:
|
||||||
}
|
}
|
||||||
// Transform an assignment with an intra timing control into a timing control with the
|
// Transform an assignment with an intra timing control into a timing control with the
|
||||||
// assignment under it
|
// assignment under it
|
||||||
AstNodeStmt* factorOutTimingControl(AstNodeAssign* nodep) const {
|
AstNode* factorOutTimingControl(AstNodeAssign* nodep) const {
|
||||||
AstNodeStmt* stmtp = nodep;
|
AstNode* stmtp = nodep;
|
||||||
AstDelay* delayp = getLhsNetDelay(nodep);
|
AstDelay* delayp = getLhsNetDelay(nodep);
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstNode* const controlp = nodep->timingControlp();
|
AstNode* const controlp = nodep->timingControlp();
|
||||||
|
|
@ -182,6 +182,11 @@ private:
|
||||||
stmtp->replaceWith(eventControlp);
|
stmtp->replaceWith(eventControlp);
|
||||||
eventControlp->addStmtsp(stmtp);
|
eventControlp->addStmtsp(stmtp);
|
||||||
stmtp = eventControlp;
|
stmtp = eventControlp;
|
||||||
|
} else if (auto* const beginp = VN_CAST(controlp, Begin)) {
|
||||||
|
// Begin from V3AssertPre
|
||||||
|
stmtp->replaceWith(beginp);
|
||||||
|
beginp->addStmtsp(stmtp);
|
||||||
|
stmtp = beginp;
|
||||||
}
|
}
|
||||||
return stmtp == nodep ? nullptr : stmtp;
|
return stmtp == nodep ? nullptr : stmtp;
|
||||||
}
|
}
|
||||||
|
|
@ -495,6 +500,8 @@ private:
|
||||||
m_procp->user2(true);
|
m_procp->user2(true);
|
||||||
}
|
}
|
||||||
void visit(AstDelay* nodep) override {
|
void visit(AstDelay* nodep) override {
|
||||||
|
UASSERT_OBJ(!nodep->isCycleDelay(), nodep,
|
||||||
|
"Cycle delays should have been handled in V3AssertPre");
|
||||||
FileLine* const flp = nodep->fileline();
|
FileLine* const flp = nodep->fileline();
|
||||||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||||
auto* const constp = VN_CAST(valuep, Const);
|
auto* const constp = VN_CAST(valuep, Const);
|
||||||
|
|
|
||||||
|
|
@ -5374,6 +5374,12 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void visit(AstClockingItem* nodep) override {
|
||||||
|
nodep->exprp()->foreach([nodep](AstVarRef* const refp) {
|
||||||
|
refp->access(nodep->direction().isWritable() ? VAccess::WRITE : VAccess::READ);
|
||||||
|
});
|
||||||
|
userIterateChildren(nodep, WidthVP{SELF, PRELIM}.p());
|
||||||
|
}
|
||||||
void visit(AstWait* nodep) override {
|
void visit(AstWait* nodep) override {
|
||||||
if (VN_IS(m_ftaskp, Func)) {
|
if (VN_IS(m_ftaskp, Func)) {
|
||||||
nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
|
nodep->v3error("Wait statements are not legal in functions. Suggest use a task "
|
||||||
|
|
|
||||||
|
|
@ -637,6 +637,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||||
|
|
||||||
<VA5,SAX>{
|
<VA5,SAX>{
|
||||||
/* Generic unsupported warnings */
|
/* Generic unsupported warnings */
|
||||||
|
"1step" { FL; return ya1STEP; }
|
||||||
"above" { ERROR_RSVD_WORD("AMS"); }
|
"above" { ERROR_RSVD_WORD("AMS"); }
|
||||||
"abs" { ERROR_RSVD_WORD("AMS"); }
|
"abs" { ERROR_RSVD_WORD("AMS"); }
|
||||||
"absdelay" { ERROR_RSVD_WORD("AMS"); }
|
"absdelay" { ERROR_RSVD_WORD("AMS"); }
|
||||||
|
|
@ -929,8 +930,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
||||||
return yaTIMENUM;
|
return yaTIMENUM;
|
||||||
}
|
}
|
||||||
1step {
|
1step {
|
||||||
FL; yylval.cdouble = 0; // Unsupported
|
return ya1STEP;
|
||||||
return yaTIMENUM;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
190
src/verilog.y
190
src/verilog.y
|
|
@ -243,6 +243,19 @@ public:
|
||||||
fileline->v3error("Unsupported DPI type '" << str << "': Use 'DPI-C'");
|
fileline->v3error("Unsupported DPI type '" << str << "': Use 'DPI-C'");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
// Given a list of clocking declarations, put them in clocking items
|
||||||
|
AstClockingItem* makeClockingItemList(FileLine* flp, const VDirection direction,
|
||||||
|
AstNodeExpr* skewp, AstNode* const clockingDeclps) {
|
||||||
|
AstClockingItem* itemsp = nullptr;
|
||||||
|
for (AstNode *nodep = clockingDeclps, *nextp; nodep; nodep = nextp) {
|
||||||
|
nextp = nodep->nextp();
|
||||||
|
if (nextp) nextp->unlinkFrBackWithNext();
|
||||||
|
if (itemsp && skewp) skewp = skewp->cloneTree(false);
|
||||||
|
AstClockingItem* itemp = new AstClockingItem{flp, direction, skewp, nodep};
|
||||||
|
itemsp = itemsp ? itemsp->addNext(itemp) : itemp;
|
||||||
|
}
|
||||||
|
return itemsp;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const VBasicDTypeKwd LOGIC = VBasicDTypeKwd::LOGIC; // Shorthand "LOGIC"
|
const VBasicDTypeKwd LOGIC = VBasicDTypeKwd::LOGIC; // Shorthand "LOGIC"
|
||||||
|
|
@ -494,6 +507,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
||||||
// for example yP_ for punctuation based operators.
|
// for example yP_ for punctuation based operators.
|
||||||
// Double underscores "yX__Y" means token X followed by Y,
|
// Double underscores "yX__Y" means token X followed by Y,
|
||||||
// and "yX__ETC" means X folled by everything but Y(s).
|
// and "yX__ETC" means X folled by everything but Y(s).
|
||||||
|
%token<fl> ya1STEP "1step"
|
||||||
//UNSUP %token<fl> yACCEPT_ON "accept_on"
|
//UNSUP %token<fl> yACCEPT_ON "accept_on"
|
||||||
%token<fl> yALIAS "alias"
|
%token<fl> yALIAS "alias"
|
||||||
%token<fl> yALWAYS "always"
|
%token<fl> yALWAYS "always"
|
||||||
|
|
@ -2828,13 +2842,13 @@ delay_controlE<delayp>:
|
||||||
|
|
||||||
delay_control<delayp>: //== IEEE: delay_control
|
delay_control<delayp>: //== IEEE: delay_control
|
||||||
'#' delay_value
|
'#' delay_value
|
||||||
{ $$ = new AstDelay{$<fl>1, $2}; }
|
{ $$ = new AstDelay{$<fl>1, $2, false}; }
|
||||||
| '#' '(' minTypMax ')'
|
| '#' '(' minTypMax ')'
|
||||||
{ $$ = new AstDelay{$<fl>1, $3}; }
|
{ $$ = new AstDelay{$<fl>1, $3, false}; }
|
||||||
| '#' '(' minTypMax ',' minTypMax ')'
|
| '#' '(' minTypMax ',' minTypMax ')'
|
||||||
{ $$ = new AstDelay{$<fl>1, $3}; RISEFALLDLYUNSUP($3); DEL($5); }
|
{ $$ = new AstDelay{$<fl>1, $3, false}; RISEFALLDLYUNSUP($3); DEL($5); }
|
||||||
| '#' '(' minTypMax ',' minTypMax ',' minTypMax ')'
|
| '#' '(' minTypMax ',' minTypMax ',' minTypMax ')'
|
||||||
{ $$ = new AstDelay{$<fl>1, $3}; RISEFALLDLYUNSUP($3); DEL($5); DEL($7); }
|
{ $$ = new AstDelay{$<fl>1, $3, false}; RISEFALLDLYUNSUP($3); DEL($5); DEL($7); }
|
||||||
;
|
;
|
||||||
|
|
||||||
delay_value<nodeExprp>: // ==IEEE:delay_value
|
delay_value<nodeExprp>: // ==IEEE:delay_value
|
||||||
|
|
@ -3320,6 +3334,10 @@ statement_item<nodep>: // IEEE: statement_item
|
||||||
// // IEEE: nonblocking_assignment
|
// // IEEE: nonblocking_assignment
|
||||||
| fexprLvalue yP_LTE delay_or_event_controlE expr ';'
|
| fexprLvalue yP_LTE delay_or_event_controlE expr ';'
|
||||||
{ $$ = new AstAssignDly{$2, $1, $4, $3}; }
|
{ $$ = new AstAssignDly{$2, $1, $4, $3}; }
|
||||||
|
// // IEEE: clocking_drive ';'
|
||||||
|
| fexprLvalue yP_LTE cycle_delay expr ';'
|
||||||
|
{ $$ = new AstAssignDly{$2, $1, $4, $3}; }
|
||||||
|
//UNSUP cycle_delay fexprLvalue yP_LTE ';' { UNSUP }
|
||||||
| yASSIGN idClassSel '=' delay_or_event_controlE expr ';'
|
| yASSIGN idClassSel '=' delay_or_event_controlE expr ';'
|
||||||
{ $$ = new AstAssign{$1, $2, $5, $4}; }
|
{ $$ = new AstAssign{$1, $2, $5, $4}; }
|
||||||
| yDEASSIGN variable_lvalue ';'
|
| yDEASSIGN variable_lvalue ';'
|
||||||
|
|
@ -3440,8 +3458,14 @@ statement_item<nodep>: // IEEE: statement_item
|
||||||
if ($2 && $2->nextp()) nextp = $2->nextp()->unlinkFrBackWithNext();
|
if ($2 && $2->nextp()) nextp = $2->nextp()->unlinkFrBackWithNext();
|
||||||
$$ = new AstEventControl{FILELINE_OR_CRE($1), $1, $2};
|
$$ = new AstEventControl{FILELINE_OR_CRE($1), $1, $2};
|
||||||
addNextNull($$, nextp); }
|
addNextNull($$, nextp); }
|
||||||
//UNSUP cycle_delay stmtBlock { UNSUP }
|
| cycle_delay stmtBlock
|
||||||
//
|
{ AstNode* nextp = nullptr;
|
||||||
|
if ($2) {
|
||||||
|
if ($2->nextp()) nextp = $2->nextp()->unlinkFrBackWithNext();
|
||||||
|
$1->addStmtsp($2);
|
||||||
|
}
|
||||||
|
$$ = $1;
|
||||||
|
addNextNull($$, nextp); }
|
||||||
| seq_block { $$ = $1; }
|
| seq_block { $$ = $1; }
|
||||||
//
|
//
|
||||||
// // IEEE: wait_statement
|
// // IEEE: wait_statement
|
||||||
|
|
@ -3452,13 +3476,6 @@ statement_item<nodep>: // IEEE: statement_item
|
||||||
// // IEEE: procedural_assertion_statement
|
// // IEEE: procedural_assertion_statement
|
||||||
| procedural_assertion_statement { $$ = $1; }
|
| procedural_assertion_statement { $$ = $1; }
|
||||||
//
|
//
|
||||||
// // IEEE: clocking_drive ';'
|
|
||||||
// // Pattern w/o cycle_delay handled by nonblocking_assign above
|
|
||||||
// // clockvar_expression made to fexprLvalue to prevent reduce conflict
|
|
||||||
// // Note LTE in this context is highest precedence, so first on left wins
|
|
||||||
//UNSUP cycle_delay fexprLvalue yP_LTE ';' { UNSUP }
|
|
||||||
//UNSUP fexprLvalue yP_LTE cycle_delay expr ';' { UNSUP }
|
|
||||||
//
|
|
||||||
//UNSUP randsequence_statement { $$ = $1; }
|
//UNSUP randsequence_statement { $$ = $1; }
|
||||||
//
|
//
|
||||||
// // IEEE: randcase_statement
|
// // IEEE: randcase_statement
|
||||||
|
|
@ -5403,87 +5420,92 @@ endLabelE<strp>:
|
||||||
//************************************************
|
//************************************************
|
||||||
// Clocking
|
// Clocking
|
||||||
|
|
||||||
clocking_declaration<nodep>: // IEEE: clocking_declaration (INCOMPLETE)
|
clocking_declaration<nodep>: // IEEE: clocking_declaration
|
||||||
//UNSUP: vvv remove this -- vastly simplified grammar:
|
yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||||
yDEFAULT yCLOCKING '@' '(' senitemEdge ')' ';' yENDCLOCKING
|
{ $$ = new AstClocking{$<fl>2, *$2, $3, $5, false}; }
|
||||||
{ $$ = new AstClocking{$2, $5, nullptr}; }
|
| yDEFAULT yCLOCKING clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||||
//UNSUP: ^^^ remove this -- vastly simplified grammar:
|
{ $$ = new AstClocking{$<fl>2, "", $3, $5, true}; }
|
||||||
//UNSUP clockingFront clocking_event ';'
|
| yDEFAULT yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||||
//UNSUP clocking_itemListE yENDCLOCKING endLabelE { SYMP->popScope($$); }
|
{ $$ = new AstClocking{$<fl>3, *$3, $4, $6, true}; }
|
||||||
|
| yGLOBAL__CLOCKING yCLOCKING clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||||
|
{ $$ = nullptr;
|
||||||
|
BBUNSUP($<fl>2, "Unsupported: global clocking"); }
|
||||||
|
| yGLOBAL__CLOCKING yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||||
|
{ $$ = nullptr;
|
||||||
|
BBUNSUP($<fl>3, "Unsupported: global clocking"); }
|
||||||
;
|
;
|
||||||
|
|
||||||
//UNSUPclockingFront: // IEEE: part of class_declaration
|
clocking_event<senItemp>: // IEEE: clocking_event
|
||||||
//UNSUP yCLOCKING { PARSEP->symPushNewAnon(VAstType::CLOCKING); }
|
'@' id
|
||||||
//UNSUP | yCLOCKING idAny/*clocking_identifier*/ { SYMP->pushNew($$); }
|
{ $$ = new AstSenItem{$<fl>2, VEdgeType::ET_CHANGED, new AstParseRef{$<fl>2, VParseRefExp::PX_TEXT, *$2, nullptr, nullptr}}; }
|
||||||
//UNSUP | yDEFAULT yCLOCKING { PARSEP->symPushNewAnon(VAstType::CLOCKING); }
|
| '@' '(' event_expression ')' { $$ = $3; }
|
||||||
//UNSUP | yDEFAULT yCLOCKING idAny/*clocking_identifier*/ { SYMP->pushNew($$); }
|
;
|
||||||
//UNSUP | yGLOBAL__CLOCKING yCLOCKING { PARSEP->symPushNewAnon(VAstType::CLOCKING); }
|
|
||||||
//UNSUP | yGLOBAL__CLOCKING yCLOCKING idAny/*clocking_identifier*/ { SYMP->pushNew($$); }
|
|
||||||
//UNSUP ;
|
|
||||||
|
|
||||||
//UNSUPclocking_event: // ==IEEE: clocking_event
|
clocking_itemListE<clockingItemp>:
|
||||||
//UNSUP '@' id { }
|
/* empty */ { $$ = nullptr; }
|
||||||
//UNSUP | '@' '(' event_expression ')' { }
|
| clocking_itemList { $$ = $1; }
|
||||||
//UNSUP ;
|
;
|
||||||
|
|
||||||
//UNSUPclocking_itemListE:
|
clocking_itemList<clockingItemp>: // IEEE: [ clocking_item ]
|
||||||
//UNSUP /* empty */ { $$ = nullptr; }
|
clocking_item { $$ = $1; }
|
||||||
//UNSUP | clocking_itemList { $$ = $1; }
|
| clocking_itemList clocking_item { if ($1) $$ = addNextNull($1, $2); }
|
||||||
//UNSUP ;
|
;
|
||||||
|
|
||||||
//UNSUPclocking_itemList: // IEEE: [ clocking_item ]
|
clocking_item<clockingItemp>: // IEEE: clocking_item
|
||||||
//UNSUP clocking_item { $$ = $1; }
|
yDEFAULT yINPUT clocking_skew ';' { $$ = new AstClockingItem{$<fl>1, VDirection::INPUT, $3, nullptr}; }
|
||||||
//UNSUP | clocking_itemList clocking_item { $$ = addNextNull($1, $2); }
|
| yDEFAULT yOUTPUT clocking_skew ';' { $$ = new AstClockingItem{$<fl>1, VDirection::OUTPUT, $3, nullptr}; }
|
||||||
//UNSUP ;
|
| yDEFAULT yINPUT clocking_skew yOUTPUT clocking_skew ';'
|
||||||
|
{ $$ = new AstClockingItem{$<fl>1, VDirection::INPUT, $3, nullptr};
|
||||||
|
$$->addNext(new AstClockingItem{$<fl>4, VDirection::OUTPUT, $5, nullptr}); }
|
||||||
|
| yINPUT clocking_skewE list_of_clocking_decl_assign ';'
|
||||||
|
{ $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::INPUT, $2, $3); }
|
||||||
|
| yOUTPUT clocking_skewE list_of_clocking_decl_assign ';'
|
||||||
|
{ $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::OUTPUT, $2, $3); }
|
||||||
|
| yINPUT clocking_skewE yOUTPUT clocking_skewE list_of_clocking_decl_assign ';'
|
||||||
|
{ $$ = nullptr;
|
||||||
|
BBUNSUP($5, "Unsupported: Mixed input/output clocking items"); }
|
||||||
|
| yINOUT list_of_clocking_decl_assign ';'
|
||||||
|
{ $$ = nullptr;
|
||||||
|
BBUNSUP($1, "Unsupported: 'inout' clocking items"); }
|
||||||
|
| assertion_item_declaration
|
||||||
|
{ $$ = nullptr;
|
||||||
|
BBUNSUP($1, "Unsupported: assertion items in clocking blocks"); }
|
||||||
|
;
|
||||||
|
|
||||||
//UNSUPclocking_item: // ==IEEE: clocking_item
|
list_of_clocking_decl_assign<nodep>: // IEEE: list_of_clocking_decl_assign
|
||||||
//UNSUP yDEFAULT default_skew ';' { }
|
clocking_decl_assign { $$ = $1; }
|
||||||
//UNSUP | clocking_direction list_of_clocking_decl_assign ';' { }
|
| list_of_clocking_decl_assign ',' clocking_decl_assign
|
||||||
//UNSUP | assertion_item_declaration { }
|
{ $$ = addNextNull($1, $3); }
|
||||||
//UNSUP ;
|
;
|
||||||
|
|
||||||
//UNSUPdefault_skew: // ==IEEE: default_skew
|
clocking_decl_assign<nodep>: // IEEE: clocking_decl_assign
|
||||||
//UNSUP yINPUT clocking_skew { }
|
idAny/*new-signal_identifier*/ exprEqE
|
||||||
//UNSUP | yOUTPUT clocking_skew { }
|
{ AstParseRef* const refp = new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1, nullptr, nullptr};
|
||||||
//UNSUP | yINPUT clocking_skew yOUTPUT clocking_skew { }
|
$$ = refp;
|
||||||
//UNSUP ;
|
if ($2) $$ = new AstAssign{$<fl>2, refp, $2}; }
|
||||||
|
;
|
||||||
|
|
||||||
//UNSUPclocking_direction: // ==IEEE: clocking_direction
|
clocking_skewE<nodeExprp>: // IEEE: [clocking_skew]
|
||||||
//UNSUP yINPUT clocking_skewE { }
|
/* empty */ { $$ = nullptr; }
|
||||||
//UNSUP | yOUTPUT clocking_skewE { }
|
| clocking_skew { $$ = $1; }
|
||||||
//UNSUP | yINPUT clocking_skewE yOUTPUT clocking_skewE { }
|
;
|
||||||
//UNSUP | yINOUT { }
|
|
||||||
//UNSUP ;
|
|
||||||
|
|
||||||
//UNSUPlist_of_clocking_decl_assign: // ==IEEE: list_of_clocking_decl_assign
|
clocking_skew<nodeExprp>: // IEEE: clocking_skew
|
||||||
//UNSUP clocking_decl_assign { $$ = $1; }
|
delay_control { $$ = $1->lhsp()->unlinkFrBack(); $1->deleteTree(); }
|
||||||
//UNSUP | list_of_clocking_decl_assign ',' clocking_decl_assign { }
|
| '#' ya1STEP { $$ = new AstConst{$<fl>1, AstConst::OneStep{}}; }
|
||||||
//UNSUP ;
|
| yPOSEDGE delay_controlE { $$ = nullptr;
|
||||||
|
BBUNSUP($1, "Unsupported: clocking event edge override"); }
|
||||||
|
| yNEGEDGE delay_controlE { $$ = nullptr;
|
||||||
|
BBUNSUP($1, "Unsupported: clocking event edge override"); }
|
||||||
|
| yEDGE delay_controlE { $$ = nullptr;
|
||||||
|
BBUNSUP($1, "Unsupported: clocking event edge override"); }
|
||||||
|
;
|
||||||
|
|
||||||
//UNSUPclocking_decl_assign: // ==IEEE: clocking_decl_assign
|
cycle_delay<delayp>: // IEEE: cycle_delay
|
||||||
//UNSUP idAny/*new-signal_identifier*/ exprEqE { $$ = $1; }
|
yP_POUNDPOUND yaINTNUM { $$ = new AstDelay{$<fl>1, new AstConst{$<fl>2, *$2}, true}; }
|
||||||
//UNSUP ;
|
| yP_POUNDPOUND id { $$ = new AstDelay{$<fl>1, new AstParseRef{$<fl>2, VParseRefExp::PX_TEXT, *$2, nullptr, nullptr}, true}; }
|
||||||
|
| yP_POUNDPOUND '(' expr ')' { $$ = new AstDelay{$<fl>1, $3, true}; }
|
||||||
//UNSUPclocking_skewE: // IEEE: [clocking_skew]
|
;
|
||||||
//UNSUP /* empty */ { $$ = nullptr; }
|
|
||||||
//UNSUP | clocking_skew { $$ = $1; }
|
|
||||||
//UNSUP ;
|
|
||||||
|
|
||||||
//UNSUPclocking_skew: // ==IEEE: clocking_skew
|
|
||||||
//UNSUP yPOSEDGE { }
|
|
||||||
//UNSUP | yPOSEDGE delay_control { }
|
|
||||||
//UNSUP | yNEGEDGE { }
|
|
||||||
//UNSUP | yNEGEDGE delay_control { }
|
|
||||||
//UNSUP | yEDGE { NEED_S09($<fl>1, "edge"); }
|
|
||||||
//UNSUP | yEDGE delay_control { NEED_S09($<fl>1, "edge"); }
|
|
||||||
//UNSUP | delay_control { $$ = $1; }
|
|
||||||
//UNSUP ;
|
|
||||||
|
|
||||||
//UNSUPcycle_delay: // ==IEEE: cycle_delay
|
|
||||||
//UNSUP yP_POUNDPOUND yaINTNUM { }
|
|
||||||
//UNSUP | yP_POUNDPOUND id { }
|
|
||||||
//UNSUP | yP_POUNDPOUND '(' expr ')' { }
|
|
||||||
//UNSUP ;
|
|
||||||
|
|
||||||
//************************************************
|
//************************************************
|
||||||
// Asserts
|
// Asserts
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,5 @@
|
||||||
|
%Error: t/t_clocking_bad1.v:16:12: Only one default clocking block allowed per module (IEEE 1800-2017 14.12)
|
||||||
|
: ... In instance t
|
||||||
|
16 | default clocking @(posedge clk);
|
||||||
|
| ^~~~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
default clocking @(posedge clk);
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
default clocking @(posedge clk);
|
||||||
|
endclocking
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,16 @@
|
||||||
|
%Error: t/t_clocking_bad2.v:15:32: 1step not allowed as output skew
|
||||||
|
15 | default input #1 output #1step;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad2.v:16:23: Multiple default input skews not allowed
|
||||||
|
16 | default input #2 output #2;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad2.v:16:33: Multiple default output skews not allowed
|
||||||
|
16 | default input #2 output #2;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad2.v:17:15: 1step not allowed as output skew
|
||||||
|
17 | output #1step out;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad2.v:18:8: Multiple clockvars with the same name not allowed
|
||||||
|
18 | output out;
|
||||||
|
| ^~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
logic in, out;
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
default input #1 output #1step;
|
||||||
|
default input #2 output #2;
|
||||||
|
output #1step out;
|
||||||
|
output out;
|
||||||
|
endclocking
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
%Error: t/t_clocking_bad3.v:14:14: Corresponding variable 'in' does not exist
|
||||||
|
14 | input in;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_clocking_bad3.v:15:15: Corresponding variable 'out' does not exist
|
||||||
|
15 | output out;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_clocking_bad3.v:18:13: Duplicate declaration of CLOCKING 'cb': 'cb'
|
||||||
|
18 | clocking cb @(posedge clk);
|
||||||
|
| ^~
|
||||||
|
t/t_clocking_bad3.v:13:13: ... Location of original declaration
|
||||||
|
13 | clocking cb @(posedge clk);
|
||||||
|
| ^~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
input in;
|
||||||
|
output out;
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
endclocking
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,45 @@
|
||||||
|
%Error: t/t_clocking_bad4.v:23:15: Skew must be constant (IEEE 1800-2017 14.4)
|
||||||
|
: ... In instance t
|
||||||
|
23 | input #cyc in;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_clocking_bad4.v:24:16: Skew cannot be negative
|
||||||
|
: ... In instance t
|
||||||
|
24 | input #(-1) out;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad4.v:31:11: Usage of cycle delays requires default clocking (IEEE 1800-2017 14.11)
|
||||||
|
: ... In instance t
|
||||||
|
31 | always ##1;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_clocking_bad4.v:32:15: Only non-blocking assignments can write to clockvars (IEEE 1800-2017 14.16)
|
||||||
|
: ... In instance t
|
||||||
|
32 | always cb1.out = clk;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_clocking_bad4.v:33:15: Only non-blocking assignments can write to clockvars (IEEE 1800-2017 14.16)
|
||||||
|
: ... In instance t
|
||||||
|
33 | assign cb1.out = clk;
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_clocking_bad4.v:34:21: Only non-blocking assignments can write to clockvars (IEEE 1800-2017 14.16)
|
||||||
|
: ... In instance t
|
||||||
|
34 | always write(cb1.out);
|
||||||
|
| ^~~
|
||||||
|
%Error: t/t_clocking_bad4.v:35:22: Event controls cannot be used in synchronous drives (IEEE 1800-2017 14.16)
|
||||||
|
: ... In instance t
|
||||||
|
35 | always cb1.out <= @(posedge clk) 1;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad4.v:36:22: Only cycle delays can be used in synchronous drives (IEEE 1800-2017 14.16)
|
||||||
|
: ... In instance t
|
||||||
|
36 | always cb1.out <= #1 1;
|
||||||
|
| ^
|
||||||
|
%Error: t/t_clocking_bad4.v:37:18: Cycle delays not allowed as intra-assignment delays (IEEE 1800-2017 14.11)
|
||||||
|
: ... In instance t
|
||||||
|
37 | always out <= ##1 1;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_clocking_bad4.v:40:12: Cannot write to input clockvar (IEEE 1800-2017 14.3)
|
||||||
|
: ... In instance t
|
||||||
|
40 | cb1.in = 1;
|
||||||
|
| ^~
|
||||||
|
%Error: t/t_clocking_bad4.v:41:21: Cannot read from output clockvar (IEEE 1800-2017 14.3)
|
||||||
|
: ... In instance t
|
||||||
|
41 | $display(cb1.out);
|
||||||
|
| ^~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,43 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
logic in, out;
|
||||||
|
clocking cb1 @(posedge clk);
|
||||||
|
input in;
|
||||||
|
output out;
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
int cyc = 0;
|
||||||
|
always @(posedge clk) cyc <= cyc + 1;
|
||||||
|
|
||||||
|
clocking cb2 @(negedge clk);
|
||||||
|
input #cyc in;
|
||||||
|
input #(-1) out;
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
task write(output x);
|
||||||
|
x = 1;
|
||||||
|
endtask
|
||||||
|
|
||||||
|
always ##1;
|
||||||
|
always cb1.out = clk;
|
||||||
|
assign cb1.out = clk;
|
||||||
|
always write(cb1.out);
|
||||||
|
always cb1.out <= @(posedge clk) 1;
|
||||||
|
always cb1.out <= #1 1;
|
||||||
|
always out <= ##1 1;
|
||||||
|
|
||||||
|
always @(posedge clk) begin
|
||||||
|
cb1.in = 1;
|
||||||
|
$display(cb1.out);
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
logic[3:0] D1, D2, Q1, Q2;
|
||||||
|
always @(posedge clk) begin
|
||||||
|
{Q1, Q2} <= {D1, D2};
|
||||||
|
end
|
||||||
|
|
||||||
|
always @(posedge clk) $display("[%0t] posedge clk", $time);
|
||||||
|
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
input #0 Q = {Q1, Q2};
|
||||||
|
output #0 D = {D1, D2};
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
initial $monitor("[%0t] --> D=%x\t\tQ=%x\t\tcb.Q=%x", $time, {D1,D2}, {Q1,Q2}, cb.Q);
|
||||||
|
|
||||||
|
int cyc = 0;
|
||||||
|
always @ (posedge clk) begin
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
if (cyc > 1 && cb.Q != {D1 - 4'd1, D2 - 4'd1}) $stop;
|
||||||
|
cb.D <= {D1 + 4'd1, D2 + 4'd1};
|
||||||
|
if (cyc==10) begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error-NOTIMING: t/t_clocking_notiming.v:11:8: Clocking output skew greater than #0 requires --timing
|
||||||
|
: ... In instance t
|
||||||
|
11 | output #1 out;
|
||||||
|
| ^~~~~~
|
||||||
|
... For error description see https://verilator.org/warn/NOTIMING?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--no-timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t;
|
||||||
|
logic clk;
|
||||||
|
logic out;
|
||||||
|
clocking cb @(posedge clk);
|
||||||
|
output #1 out;
|
||||||
|
endclocking
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,55 @@
|
||||||
|
0 | posedge
|
||||||
|
0 | cb.y=0
|
||||||
|
0 | b=0
|
||||||
|
0 | x<=0
|
||||||
|
0 | y=0
|
||||||
|
0 | c<=0
|
||||||
|
0 | c<=1
|
||||||
|
0 | cb.a=1
|
||||||
|
0 | cb.b=1
|
||||||
|
0 | posedge
|
||||||
|
0 | x<=1
|
||||||
|
0 | y=1
|
||||||
|
0 | c<=0
|
||||||
|
0 | cb.a=0
|
||||||
|
0 | cb.b=1
|
||||||
|
0 | cb.y=1
|
||||||
|
0 | b=1
|
||||||
|
0 | x<=0
|
||||||
|
0 | y=0
|
||||||
|
0 | 0 1 0 0 0 0
|
||||||
|
20 | posedge
|
||||||
|
20 | c<=1
|
||||||
|
20 | cb.a=1
|
||||||
|
20 | cb.b=1
|
||||||
|
20 | cb.y=0
|
||||||
|
20 | b=0
|
||||||
|
20 | posedge
|
||||||
|
20 | x<=1
|
||||||
|
20 | y=1
|
||||||
|
20 | c<=0
|
||||||
|
20 | cb.a=0
|
||||||
|
20 | cb.b=1
|
||||||
|
20 | cb.y=1
|
||||||
|
20 | b=1
|
||||||
|
20 | x<=0
|
||||||
|
20 | y=0
|
||||||
|
20 | 0 1 0 0 0 0
|
||||||
|
30 | posedge
|
||||||
|
30 | c<=1
|
||||||
|
30 | cb.a=1
|
||||||
|
30 | cb.b=1
|
||||||
|
30 | cb.y=0
|
||||||
|
30 | b=0
|
||||||
|
30 | posedge
|
||||||
|
30 | x<=1
|
||||||
|
30 | y=1
|
||||||
|
30 | c<=0
|
||||||
|
30 | cb.a=0
|
||||||
|
30 | cb.b=1
|
||||||
|
30 | cb.y=1
|
||||||
|
30 | b=1
|
||||||
|
30 | x<=0
|
||||||
|
30 | y=0
|
||||||
|
30 | 0 1 0 0 0 0
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
compile(
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
expect_filename => $Self->{golden_filename}
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,74 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t (/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
int cyc = 0;
|
||||||
|
|
||||||
|
always @(negedge clk) begin // negedge so there is nothing after $finish
|
||||||
|
cyc <= cyc + 1;
|
||||||
|
if (cyc == 2) begin
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
logic genclk = 0, a = 0, b = 1, c = 0, x = 0, y, z = 0;
|
||||||
|
always @(edge clk) genclk = clk;
|
||||||
|
always @(posedge genclk) $display("%0t | posedge", $time);
|
||||||
|
|
||||||
|
// Clocking block
|
||||||
|
clocking cb @(posedge genclk);
|
||||||
|
input #0 a, y;
|
||||||
|
input #1step b;
|
||||||
|
output #0 x;
|
||||||
|
`ifdef VERILATOR_TIMING
|
||||||
|
output #2 z;
|
||||||
|
`endif
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
// Print after Observed
|
||||||
|
always @(posedge genclk) a = ~a;
|
||||||
|
always @cb $display("%0t | cb.a=%b", $time, cb.a);
|
||||||
|
always @cb $display("%0t | cb.b=%b", $time, cb.b);
|
||||||
|
always @cb.y $display("%0t | cb.y=%b", $time, cb.y);
|
||||||
|
|
||||||
|
// Retrigger everything after Observed
|
||||||
|
always @cb.a b = x;
|
||||||
|
always @b begin
|
||||||
|
$display("%0t | b=%b", $time, b);
|
||||||
|
if (b == 0) genclk = ~genclk;
|
||||||
|
end
|
||||||
|
|
||||||
|
// Do an NBA
|
||||||
|
always @(posedge genclk) c <= ~c;
|
||||||
|
always @c begin
|
||||||
|
$display("%0t | c<=%b", $time, c);
|
||||||
|
end
|
||||||
|
|
||||||
|
// Print after Re-NBA
|
||||||
|
always @(posedge genclk) cb.x <= ~x;
|
||||||
|
always @x $display("%0t | x<=%b", $time, x);
|
||||||
|
|
||||||
|
// Retrigger everything after Re-NBA
|
||||||
|
always @x y = x;
|
||||||
|
always @y begin
|
||||||
|
$display("%0t | y=%b", $time, y);
|
||||||
|
if (y == 1) genclk = ~genclk;
|
||||||
|
end
|
||||||
|
|
||||||
|
`ifdef VERILATOR_TIMING
|
||||||
|
// Print after delay and Re-NBA
|
||||||
|
always @(posedge genclk) cb.z <= ~z;
|
||||||
|
always @z $display("%0t | z<=%b", $time, z);
|
||||||
|
`endif
|
||||||
|
|
||||||
|
// Print in Postponed
|
||||||
|
always @(posedge genclk) $strobe("%0t | %b %b %b %b %b %b", $time, a, b, c, x, y, z);
|
||||||
|
endmodule;
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
0 | posedge
|
||||||
|
0 | cb.y=0
|
||||||
|
0 | b=0
|
||||||
|
0 | z<=0
|
||||||
|
0 | x<=0
|
||||||
|
0 | y=0
|
||||||
|
0 | c<=0
|
||||||
|
0 | c<=1
|
||||||
|
0 | cb.a=1
|
||||||
|
0 | cb.b=1
|
||||||
|
0 | posedge
|
||||||
|
0 | x<=1
|
||||||
|
0 | y=1
|
||||||
|
0 | c<=0
|
||||||
|
0 | cb.a=0
|
||||||
|
0 | cb.b=1
|
||||||
|
0 | cb.y=1
|
||||||
|
0 | b=1
|
||||||
|
0 | x<=0
|
||||||
|
0 | y=0
|
||||||
|
0 | 0 1 0 0 0 0
|
||||||
|
2 | z<=1
|
||||||
|
3 | posedge
|
||||||
|
3 | c<=1
|
||||||
|
3 | cb.a=1
|
||||||
|
3 | cb.b=1
|
||||||
|
3 | cb.y=0
|
||||||
|
3 | b=0
|
||||||
|
3 | posedge
|
||||||
|
3 | x<=1
|
||||||
|
3 | y=1
|
||||||
|
3 | c<=0
|
||||||
|
3 | cb.a=0
|
||||||
|
3 | cb.b=1
|
||||||
|
3 | cb.y=1
|
||||||
|
3 | b=1
|
||||||
|
3 | x<=0
|
||||||
|
3 | y=0
|
||||||
|
3 | 0 1 0 0 0 1
|
||||||
|
5 | posedge
|
||||||
|
5 | z<=0
|
||||||
|
5 | c<=1
|
||||||
|
5 | cb.a=1
|
||||||
|
5 | cb.b=1
|
||||||
|
5 | cb.y=0
|
||||||
|
5 | b=0
|
||||||
|
5 | posedge
|
||||||
|
5 | x<=1
|
||||||
|
5 | y=1
|
||||||
|
5 | c<=0
|
||||||
|
5 | cb.a=0
|
||||||
|
5 | cb.b=1
|
||||||
|
5 | cb.y=1
|
||||||
|
5 | b=1
|
||||||
|
5 | x<=0
|
||||||
|
5 | y=0
|
||||||
|
5 | 0 1 0 0 0 0
|
||||||
|
*-* All Finished *-*
|
||||||
|
|
@ -0,0 +1,31 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(vlt => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
top_filename("t/t_clocking_sched.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
timing_loop => 1,
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
expect_filename => $Self->{golden_filename}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,90 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
`timescale 10ns/1ns
|
||||||
|
|
||||||
|
`ifdef TEST_VERBOSE
|
||||||
|
`define WRITE_VERBOSE(args) $write args
|
||||||
|
`else
|
||||||
|
`define WRITE_VERBOSE(args)
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef TEST_WIDTH
|
||||||
|
`define TEST_WIDTH 4
|
||||||
|
`endif
|
||||||
|
`ifndef TEST_BITS
|
||||||
|
`define TEST_BITS 4*`TEST_WIDTH
|
||||||
|
`endif
|
||||||
|
|
||||||
|
`ifndef TEST_CLK_PERIOD
|
||||||
|
`define TEST_CLK_PERIOD 10
|
||||||
|
`endif
|
||||||
|
`ifndef TEST_INPUT_SKEW
|
||||||
|
`define TEST_INPUT_SKEW 2
|
||||||
|
`endif
|
||||||
|
`ifndef TEST_OUTPUT_SKEW
|
||||||
|
`define TEST_OUTPUT_SKEW 6
|
||||||
|
`endif
|
||||||
|
`ifndef TEST_CYCLE_DELAY
|
||||||
|
`define TEST_CYCLE_DELAY 4
|
||||||
|
`endif
|
||||||
|
|
||||||
|
module t;
|
||||||
|
typedef logic[`TEST_BITS-1:0] sig_t;
|
||||||
|
sig_t D, Q;
|
||||||
|
always @(posedge clk) Q <= D;
|
||||||
|
|
||||||
|
logic clk = 0;
|
||||||
|
always #(`TEST_CLK_PERIOD/2) clk = ~clk;
|
||||||
|
|
||||||
|
always @(posedge clk) `WRITE_VERBOSE(("[%0t] posedge clk\n", $time));
|
||||||
|
|
||||||
|
default clocking cb @(posedge clk);
|
||||||
|
default input #`TEST_INPUT_SKEW output #`TEST_OUTPUT_SKEW;
|
||||||
|
input Q;
|
||||||
|
output D;
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
`ifdef TEST_VERBOSE
|
||||||
|
initial $monitor("[%0t] --> D=%x\t\tQ=%x\t\tcb.Q=%x", $time, D, Q, cb.Q);
|
||||||
|
`endif
|
||||||
|
|
||||||
|
always
|
||||||
|
begin
|
||||||
|
sig_t val = '0;
|
||||||
|
cb.D <= val;
|
||||||
|
for (int i = 0; i < 5; i++) begin ##(`TEST_CYCLE_DELAY+`TEST_OUTPUT_SKEW/`TEST_CLK_PERIOD+1)
|
||||||
|
val = {`TEST_WIDTH{(`TEST_BITS/`TEST_WIDTH)'('ha + i)}};
|
||||||
|
`WRITE_VERBOSE(("[%0t] cb.D <= ##%0d %x\n", $time, `TEST_CYCLE_DELAY, val));
|
||||||
|
cb.D <= ##(`TEST_CYCLE_DELAY) val;
|
||||||
|
fork
|
||||||
|
#(`TEST_CYCLE_DELAY*`TEST_CLK_PERIOD+`TEST_OUTPUT_SKEW-0.1) begin
|
||||||
|
if (D == val) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] D == %x == %x\n", $time, D, val));
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
if (cb.Q != D) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] cb.Q == %x != %x\n", $time, cb.Q, D));
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#(`TEST_CYCLE_DELAY*`TEST_CLK_PERIOD+`TEST_OUTPUT_SKEW+0.1) begin
|
||||||
|
if (D != val) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] D == %x != %x\n", $time, D, val));
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
if (cb.Q == D) begin
|
||||||
|
`WRITE_VERBOSE(("[%0t] cb.Q == %x == %x\n", $time, cb.Q, D));
|
||||||
|
$stop;
|
||||||
|
end
|
||||||
|
end
|
||||||
|
join_none
|
||||||
|
end
|
||||||
|
##4
|
||||||
|
$write("*-* All Finished *-*\n");
|
||||||
|
$finish;
|
||||||
|
end
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
top_filename("t/t_clocking_timing.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--exe --main --timing"],
|
||||||
|
make_main => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
if (!$Self->have_coroutines) {
|
||||||
|
skip("No coroutine support");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
top_filename("t/t_clocking_timing.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
verilator_flags2 => ["--exe --main --timing -DTEST_INPUT_SKEW=12 -DTEST_OUTPUT_SKEW=16"],
|
||||||
|
make_main => 0,
|
||||||
|
);
|
||||||
|
|
||||||
|
execute(
|
||||||
|
check_finished => 1,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:14:26: Unsupported: Mixed input/output clocking items
|
||||||
|
14 | input #1 output #1step x;
|
||||||
|
| ^
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:15:8: Unsupported: 'inout' clocking items
|
||||||
|
15 | inout y;
|
||||||
|
| ^~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:16:15: Unsupported: clocking event edge override
|
||||||
|
16 | output posedge #1 a;
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:17:15: Unsupported: clocking event edge override
|
||||||
|
17 | output negedge #1 b;
|
||||||
|
| ^~~~~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:18:15: Unsupported: clocking event edge override
|
||||||
|
18 | output edge #1 b;
|
||||||
|
| ^~~~
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:13:20: Unsupported: global clocking
|
||||||
|
13 | global clocking cb @(posedge clk);
|
||||||
|
| ^~
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:21:11: Unsupported: global clocking
|
||||||
|
21 | global clocking @(posedge clk);
|
||||||
|
| ^~~~~~~~
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,23 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
global clocking cb @(posedge clk);
|
||||||
|
input #1 output #1step x;
|
||||||
|
inout y;
|
||||||
|
output posedge #1 a;
|
||||||
|
output negedge #1 b;
|
||||||
|
output edge #1 b;
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
global clocking @(posedge clk);
|
||||||
|
endclocking
|
||||||
|
endmodule
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error-UNSUPPORTED: t/t_clocking_unsup2.v:16:11: Unsupported: ##0 delays
|
||||||
|
: ... In instance t
|
||||||
|
16 | always ##0;
|
||||||
|
| ^~
|
||||||
|
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(linter => 1);
|
||||||
|
|
||||||
|
lint(
|
||||||
|
verilator_flags2 => ["--timing"],
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
// DESCRIPTION: Verilator: Verilog Test module
|
||||||
|
//
|
||||||
|
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||||
|
// any use, without warranty, 2022 by Antmicro Ltd.
|
||||||
|
// SPDX-License-Identifier: CC0-1.0
|
||||||
|
|
||||||
|
module t(/*AUTOARG*/
|
||||||
|
// Inputs
|
||||||
|
clk
|
||||||
|
);
|
||||||
|
input clk;
|
||||||
|
|
||||||
|
default clocking @(posedge clk);
|
||||||
|
endclocking
|
||||||
|
|
||||||
|
always ##0;
|
||||||
|
endmodule
|
||||||
|
|
@ -94,12 +94,9 @@ module Test2 (/*AUTOARG*/
|
||||||
end
|
end
|
||||||
|
|
||||||
default clocking @(posedge clk); endclocking
|
default clocking @(posedge clk); endclocking
|
||||||
|
|
||||||
assert property ($rose(dly0[0]) || dly0%2==0);
|
assert property ($rose(dly0[0]) || dly0%2==0);
|
||||||
|
|
||||||
default clocking @(posedge clk); endclocking
|
|
||||||
assert property ($fell(dly1[0]) || dly1%2==1);
|
assert property ($fell(dly1[0]) || dly1%2==1);
|
||||||
|
|
||||||
default clocking @(posedge clk); endclocking
|
|
||||||
assert property ($stable(dly2[31:4]));
|
assert property ($stable(dly2[31:4]));
|
||||||
assert property (!$changed(dly2[31:4]));
|
assert property (!$changed(dly2[31:4]));
|
||||||
endmodule
|
endmodule
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
%Error-NEEDTIMINGOPT: t/t_clocking_notiming.v:11:8: Use --timing or --no-timing to specify how clocking output skew greater than #0 should be handled
|
||||||
|
: ... In instance t
|
||||||
|
11 | output #1 out;
|
||||||
|
| ^~~~~~
|
||||||
|
... For error description see https://verilator.org/warn/NEEDTIMINGOPT?v=latest
|
||||||
|
%Error: Exiting due to
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#!/usr/bin/env perl
|
||||||
|
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
|
||||||
|
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
|
||||||
|
#
|
||||||
|
# Copyright 2022 by Antmicro Ltd. This program is free software; you
|
||||||
|
# can redistribute it and/or modify it under the terms of either the GNU
|
||||||
|
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||||
|
# Version 2.0.
|
||||||
|
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||||
|
|
||||||
|
scenarios(simulator => 1);
|
||||||
|
|
||||||
|
top_filename("t/t_clocking_notiming.v");
|
||||||
|
|
||||||
|
compile(
|
||||||
|
# --timing/--no-timing not specified
|
||||||
|
fails => 1,
|
||||||
|
expect_filename => $Self->{golden_filename},
|
||||||
|
);
|
||||||
|
|
||||||
|
ok(1);
|
||||||
|
1;
|
||||||
Loading…
Reference in New Issue