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, ...) \
|
||||
|
|
|
|||
|
|
@ -243,16 +243,21 @@ public:
|
|||
// METHODS
|
||||
AstScope* scopep() { return m_scopep; }
|
||||
|
||||
// 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{}}};
|
||||
// Make a new AstActive sensitive to the given sentree and return it
|
||||
AstActive* makeActive(FileLine* const fl, AstSenTree* const senTreep) {
|
||||
auto* const activep = new AstActive{fl, "", senTreep};
|
||||
activep->sensesStorep(activep->sensesp());
|
||||
addActive(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)
|
||||
template <typename SenItemKind>
|
||||
AstActive* getSpecialActive(FileLine* fl) {
|
||||
|
|
@ -542,6 +547,22 @@ private:
|
|||
AstActive* const activep = m_namer.makeSpecialActive<AstSenItem::Combo>(nodep->fileline());
|
||||
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 {
|
||||
visitAlways(nodep, nodep->sensesp(), VAlwaysKwd::ALWAYS);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -16,6 +16,7 @@
|
|||
// Pre steps:
|
||||
// Attach clocks to each assertion
|
||||
// Substitute property references by property body (IEEE Std 1800-2012, section 16.12.1).
|
||||
// Transform clocking blocks into imperative logic
|
||||
//*************************************************************************
|
||||
|
||||
#include "config_build.h"
|
||||
|
|
@ -24,8 +25,10 @@
|
|||
#include "V3AssertPre.h"
|
||||
|
||||
#include "V3Ast.h"
|
||||
#include "V3Const.h"
|
||||
#include "V3Global.h"
|
||||
#include "V3Task.h"
|
||||
#include "V3UniqueNames.h"
|
||||
|
||||
VL_DEFINE_DEBUG_FUNCTIONS;
|
||||
|
||||
|
|
@ -37,16 +40,26 @@ class AssertPreVisitor final : public VNVisitor {
|
|||
// Eventually inlines calls to sequences, properties, etc.
|
||||
// We're not parsing the tree, or anything more complicated.
|
||||
private:
|
||||
// NODE STATE/TYPES
|
||||
// NODE STATE
|
||||
const VNUser1InUse m_inuser1;
|
||||
// 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:
|
||||
AstSenItem* m_seniDefaultp = nullptr; // Default sensitivity (from AstDefClock)
|
||||
AstClocking* m_defaultClockingp = nullptr; // Default clocking for the current module
|
||||
// Reset each assertion:
|
||||
AstSenItem* m_senip = nullptr; // Last sensitivity
|
||||
// Reset each always:
|
||||
AstSenItem* m_seniAlwaysp = nullptr; // Last sensitivity in always
|
||||
// Reset each assertion:
|
||||
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
|
||||
|
||||
|
|
@ -55,7 +68,7 @@ private:
|
|||
// Return nullptr for always
|
||||
AstSenTree* newp = nullptr;
|
||||
AstSenItem* senip = m_senip;
|
||||
if (!senip) senip = m_seniDefaultp;
|
||||
if (!senip && m_defaultClockingp) senip = m_defaultClockingp->sensesp();
|
||||
if (!senip) senip = m_seniAlwaysp;
|
||||
if (!senip) {
|
||||
nodep->v3warn(E_UNSUPPORTED, "Unsupported: Unclocked assertion");
|
||||
|
|
@ -139,17 +152,199 @@ private:
|
|||
|
||||
// VISITORS
|
||||
//========== Statements
|
||||
void visit(AstClocking* nodep) override {
|
||||
void visit(AstClocking* const nodep) override {
|
||||
VL_RESTORER(m_clockingp);
|
||||
m_clockingp = nodep;
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
// Store the new default clock, reset on new module
|
||||
m_seniDefaultp = nodep->sensesp();
|
||||
// Trash it, keeping children
|
||||
if (nodep->bodysp()) {
|
||||
nodep->replaceWith(nodep->bodysp()->unlinkFrBack());
|
||||
} else {
|
||||
nodep->unlinkFrBack();
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstClockingItem* const nodep) override {
|
||||
FileLine* const flp = nodep->fileline();
|
||||
V3Const::constifyEdit(nodep->skewp());
|
||||
if (!VN_IS(nodep->skewp(), Const)) {
|
||||
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 {
|
||||
iterateAndNextNull(nodep->sensesp());
|
||||
|
|
@ -255,9 +450,20 @@ private:
|
|||
VL_DO_DANGLING(pushDeletep(nodep), nodep);
|
||||
}
|
||||
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);
|
||||
// Reset defaults
|
||||
m_seniDefaultp = nullptr;
|
||||
}
|
||||
void visit(AstProperty* nodep) override {
|
||||
// The body will be visited when will be substituted in place of property reference
|
||||
|
|
@ -268,7 +474,8 @@ private:
|
|||
|
||||
public:
|
||||
// CONSTRUCTORS
|
||||
explicit AssertPreVisitor(AstNetlist* nodep) {
|
||||
explicit AssertPreVisitor(AstNetlist* nodep)
|
||||
: m_netlistp{nodep} {
|
||||
clearAssertInfo();
|
||||
// Process
|
||||
iterate(nodep);
|
||||
|
|
|
|||
|
|
@ -1122,6 +1122,50 @@ public:
|
|||
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 {
|
||||
// Unsized/open-range Array data type, ie "some_dtype var_name []"
|
||||
// @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
|
||||
initWithNumber();
|
||||
}
|
||||
class OneStep {};
|
||||
AstConst(FileLine* fl, OneStep)
|
||||
: ASTGEN_SUPER_Const(fl)
|
||||
, m_num(V3Number::OneStep{}, this) {
|
||||
dtypeSetLogicSized(64, VSigning::UNSIGNED);
|
||||
initWithNumber();
|
||||
}
|
||||
ASTGEN_MEMBERS_AstConst;
|
||||
string name() const override { return num().ascii(); } // * = 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)
|
||||
};
|
||||
class AstClocking final : public AstNode {
|
||||
// Set default clock region
|
||||
// Parents: MODULE
|
||||
// @astgen op1 := sensesp : List[AstSenItem]
|
||||
// @astgen op2 := bodysp : List[AstNode]
|
||||
// Children: SENITEM, CLOCKING ITEMs, VARs
|
||||
// @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:
|
||||
AstClocking(FileLine* fl, AstSenItem* sensesp, AstNode* bodysp)
|
||||
: ASTGEN_SUPER_Clocking(fl) {
|
||||
this->addSensesp(sensesp);
|
||||
this->addBodysp(bodysp);
|
||||
AstClocking(FileLine* fl, const std::string& name, AstSenItem* sensesp,
|
||||
AstClockingItem* itemsp, bool isDefault)
|
||||
: ASTGEN_SUPER_Clocking(fl)
|
||||
, m_isDefault{isDefault} {
|
||||
m_name = name;
|
||||
this->sensesp(sensesp);
|
||||
addItemsp(itemsp);
|
||||
}
|
||||
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 {
|
||||
// Container for const static data
|
||||
|
|
@ -2182,6 +2215,17 @@ public:
|
|||
void dump(std::ostream& str) const override;
|
||||
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 {
|
||||
// Like always but post assignments for memory assignment IFs
|
||||
// @astgen op1 := sensesp : Optional[AstSenTree] // Sensitivity list iff clocked
|
||||
|
|
@ -2200,6 +2244,17 @@ public:
|
|||
: ASTGEN_SUPER_AlwaysPostponed(fl, stmtsp) {}
|
||||
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 {
|
||||
public:
|
||||
AstFinal(FileLine* fl, AstNode* stmtsp)
|
||||
|
|
@ -2496,13 +2551,18 @@ class AstDelay final : public AstNodeStmt {
|
|||
// Delay statement
|
||||
// @astgen op1 := lhsp : AstNodeExpr // Delay value
|
||||
// @astgen op2 := stmtsp : List[AstNode] // Statements under delay
|
||||
const bool m_isCycle; // True if it is a cycle delay
|
||||
|
||||
public:
|
||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp)
|
||||
: ASTGEN_SUPER_Delay(fl) {
|
||||
AstDelay(FileLine* fl, AstNodeExpr* lhsp, bool isCycle)
|
||||
: ASTGEN_SUPER_Delay(fl)
|
||||
, m_isCycle{isCycle} {
|
||||
this->lhsp(lhsp);
|
||||
}
|
||||
ASTGEN_MEMBERS_AstDelay;
|
||||
void dump(std::ostream& str) const override;
|
||||
bool isTimingControl() const override { return true; }
|
||||
bool isCycleDelay() const { return m_isCycle; }
|
||||
bool same(const AstNode* /*samep*/) const override { return true; }
|
||||
};
|
||||
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
|
||||
if (adtypep->boundp()) info.m_type += ", " + cvtToStr(adtypep->boundConst() + 1);
|
||||
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)) {
|
||||
info.m_type = "VlClassRef<" + EmitCBaseVisitor::prefixNameProtect(adtypep) + ">";
|
||||
} 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()
|
||||
&& 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 {
|
||||
this->AstNodeDType::dumpSmall(str);
|
||||
str << "[]";
|
||||
|
|
@ -2323,3 +2330,8 @@ AstAlways* AstAssignW::convertToAlways() {
|
|||
replaceWith(newp); // User expected to then deleteTree();
|
||||
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_unnamedScope; // Name of begin blocks, including unnamed blocks
|
||||
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
|
||||
|
||||
|
|
@ -123,11 +123,18 @@ private:
|
|||
|
||||
// VISITORS
|
||||
void visit(AstFork* nodep) override {
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = true;
|
||||
// Keep this begin to group its statements together
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = true;
|
||||
dotNames(nodep, "__FORK__");
|
||||
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 {
|
||||
VL_RESTORER(m_modp);
|
||||
{
|
||||
|
|
@ -178,15 +185,14 @@ private:
|
|||
VL_RESTORER(m_unnamedScope);
|
||||
{
|
||||
{
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = false;
|
||||
dotNames(nodep, "__BEGIN__");
|
||||
}
|
||||
UASSERT_OBJ(!nodep->genforp(), nodep, "GENFORs should have been expanded earlier");
|
||||
|
||||
// Cleanup
|
||||
if (m_underFork) {
|
||||
// If we're under a fork, keep this begin to group its statements together
|
||||
if (m_keepBegins) {
|
||||
nodep->name("");
|
||||
return;
|
||||
}
|
||||
|
|
@ -263,8 +269,8 @@ private:
|
|||
}
|
||||
// VISITORS - LINT CHECK
|
||||
void visit(AstIf* nodep) override { // not AstNodeIf; other types not covered
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = false;
|
||||
// Check IFDEPTH warning - could be in other transform files if desire
|
||||
VL_RESTORER(m_ifDepth);
|
||||
if (m_ifDepth == -1 || v3Global.opt.ifDepth() < 1) { // Turned off
|
||||
|
|
@ -279,8 +285,8 @@ private:
|
|||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstNode* nodep) override {
|
||||
VL_RESTORER(m_underFork);
|
||||
m_underFork = false;
|
||||
VL_RESTORER(m_keepBegins);
|
||||
m_keepBegins = false;
|
||||
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 {
|
||||
if (nodep->isOutputter()) m_sideEffect = true;
|
||||
iterateChildren(nodep);
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ public:
|
|||
return v3Global.opt.compLimitMembers() != 0 // Enabled
|
||||
&& !varp->isStatic() // Not a static variable
|
||||
&& !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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -660,6 +660,8 @@ string EmitCFunc::emitVarResetRecurse(const AstVar* varp, const string& varNameP
|
|||
const string cvtarray = (adtypep->subDTypep()->isWide() ? ".data()" : "");
|
||||
return emitVarResetRecurse(varp, varNameProtected, adtypep->subDTypep(), depth + 1,
|
||||
suffix + ".atDefault()" + cvtarray);
|
||||
} else if (VN_IS(dtypep, SampleQueueDType)) {
|
||||
return "";
|
||||
} else if (const AstUnpackArrayDType* const adtypep = VN_CAST(dtypep, UnpackArrayDType)) {
|
||||
UASSERT_OBJ(adtypep->hi() >= adtypep->lo(), varp,
|
||||
"Should have swapped msb & lsb earlier.");
|
||||
|
|
|
|||
|
|
@ -510,6 +510,7 @@ private:
|
|||
void visit(AstCFunc* nodep) override {
|
||||
iterateNewStmt(nodep, "User C Function", "User C Function");
|
||||
}
|
||||
void visit(AstClocking* nodep) override { iterateNewStmt(nodep, nullptr, nullptr); }
|
||||
void visit(AstSenItem* nodep) override {
|
||||
m_inSenItem = true;
|
||||
if (m_logicVertexp) { // Already under logic; presumably a SenGate
|
||||
|
|
|
|||
|
|
@ -740,6 +740,7 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
// STATE
|
||||
LinkDotState* const m_statep; // State to pass between visitors, including symbol table
|
||||
AstNodeModule* m_classOrPackagep = nullptr; // Current package
|
||||
AstClocking* m_clockingp = nullptr; // Current clocking block
|
||||
VSymEnt* m_modSymp = nullptr; // Symbol Entry for current module
|
||||
VSymEnt* m_curSymp = nullptr; // Symbol Entry for current table, where to lookup/insert
|
||||
string m_scope; // Scope text
|
||||
|
|
@ -1123,6 +1124,49 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
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 {
|
||||
// Var: Remember its name for later resolution
|
||||
UASSERT_OBJ(m_curSymp && m_modSymp, nodep, "Var not under module?");
|
||||
|
|
@ -1150,6 +1194,10 @@ class LinkDotFindVisitor final : public VNVisitor {
|
|||
<< cvtToHex(foundp->parentp()) << endl);
|
||||
if (foundp->parentp() == m_curSymp // Only when on same level
|
||||
&& !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
|
||||
= ((findvarp->isDeclTyped() && nodep->isDeclTyped())
|
||||
|| (findvarp->isIO() && nodep->isIO())); // e.g. !(output && output)
|
||||
|
|
@ -1946,6 +1994,7 @@ private:
|
|||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current function/task
|
||||
int m_modportNum = 0; // Uniqueify modport numbers
|
||||
bool m_inSens = false; // True if in senitem
|
||||
|
||||
struct DotStates {
|
||||
DotPosition m_dotPos; // Scope part of dotted resolution
|
||||
|
|
@ -2062,6 +2111,24 @@ private:
|
|||
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) {
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
void visit(AstSenItem* nodep) override {
|
||||
VL_RESTORER(m_inSens);
|
||||
m_inSens = true;
|
||||
iterateChildren(nodep);
|
||||
}
|
||||
void visit(AstParseRef* nodep) override {
|
||||
if (nodep->user3SetOnce()) return;
|
||||
UINFO(9, " linkPARSEREF " << m_ds.ascii() << " n=" << nodep << endl);
|
||||
|
|
@ -2417,6 +2489,12 @@ private:
|
|||
}
|
||||
// What fell out?
|
||||
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) {
|
||||
} else if (VN_IS(foundp->nodep(), Cell) || VN_IS(foundp->nodep(), Begin)
|
||||
|| VN_IS(foundp->nodep(), Netlist) // for $root
|
||||
|
|
@ -2582,6 +2660,9 @@ private:
|
|||
m_ds.m_dotPos = DP_MEMBER;
|
||||
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) {
|
||||
|
|
|
|||
|
|
@ -60,6 +60,8 @@ private:
|
|||
AstNodeModule* m_modp = nullptr; // Current module
|
||||
AstNodeFTask* m_ftaskp = nullptr; // Current task
|
||||
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_genblkNum = 0; // Begin block number, 0=none seen
|
||||
VLifetime m_lifetime = VLifetime::STATIC; // Propagating lifetime
|
||||
|
|
@ -632,6 +634,66 @@ private:
|
|||
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 {
|
||||
// Default: Just iterate
|
||||
|
|
|
|||
|
|
@ -494,6 +494,10 @@ V3Number& V3Number::setMask(int nbits) {
|
|||
string V3Number::ascii(bool prefixed, bool cleanVerilog) const {
|
||||
std::ostringstream out;
|
||||
|
||||
if (is1Step()) {
|
||||
out << "1step";
|
||||
return out.str();
|
||||
}
|
||||
if (isDouble()) {
|
||||
out.precision(17);
|
||||
if (VL_UNCOVERABLE(width() != 64)) {
|
||||
|
|
|
|||
|
|
@ -97,6 +97,7 @@ private:
|
|||
public:
|
||||
bool m_sized : 1; // True if the user specified the width, else we track it.
|
||||
bool m_signed : 1; // True if signed value
|
||||
bool m_is1Step : 1; // True if 1step
|
||||
bool m_isNull : 1; // True if "null" versus normal 0
|
||||
bool m_fromString : 1; // True if from string literal
|
||||
bool m_autoExtend : 1; // True if SystemVerilog extend-to-any-width
|
||||
|
|
@ -107,6 +108,7 @@ public:
|
|||
: m_type{V3NumberDataType::UNINITIALIZED}
|
||||
, m_sized{false}
|
||||
, m_signed{false}
|
||||
, m_is1Step{false}
|
||||
, m_isNull{false}
|
||||
, m_fromString{false}
|
||||
, m_autoExtend{false} {}
|
||||
|
|
@ -118,6 +120,7 @@ public:
|
|||
, m_type{other.m_type}
|
||||
, m_sized{other.m_sized}
|
||||
, m_signed{other.m_signed}
|
||||
, m_is1Step{other.m_is1Step}
|
||||
, m_isNull{other.m_isNull}
|
||||
, m_fromString{other.m_fromString}
|
||||
, m_autoExtend{other.m_autoExtend} {
|
||||
|
|
@ -145,6 +148,7 @@ public:
|
|||
m_type = other.m_type;
|
||||
m_sized = other.m_sized;
|
||||
m_signed = other.m_signed;
|
||||
m_is1Step = other.m_is1Step;
|
||||
m_isNull = other.m_isNull;
|
||||
m_fromString = other.m_fromString;
|
||||
m_autoExtend = other.m_autoExtend;
|
||||
|
|
@ -156,6 +160,7 @@ public:
|
|||
, m_type{other.m_type}
|
||||
, m_sized{other.m_sized}
|
||||
, m_signed{other.m_signed}
|
||||
, m_is1Step{other.m_is1Step}
|
||||
, m_isNull{other.m_isNull}
|
||||
, m_fromString{other.m_fromString}
|
||||
, m_autoExtend{other.m_autoExtend} {
|
||||
|
|
@ -184,6 +189,7 @@ public:
|
|||
m_type = other.m_type;
|
||||
m_sized = other.m_sized;
|
||||
m_signed = other.m_signed;
|
||||
m_is1Step = other.m_is1Step;
|
||||
m_isNull = other.m_isNull;
|
||||
m_fromString = other.m_fromString;
|
||||
m_autoExtend = other.m_autoExtend;
|
||||
|
|
@ -489,6 +495,11 @@ public:
|
|||
setString(value);
|
||||
m_data.m_fromString = true;
|
||||
}
|
||||
class OneStep {};
|
||||
V3Number(OneStep, AstNode* nodep) {
|
||||
init(nodep, 64);
|
||||
m_data.m_is1Step = true;
|
||||
}
|
||||
class Null {};
|
||||
V3Number(Null, AstNode* nodep) {
|
||||
init(nodep);
|
||||
|
|
@ -601,6 +612,7 @@ public:
|
|||
|| m_data.type() == V3NumberDataType::DOUBLE;
|
||||
}
|
||||
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 isFourState() const VL_MT_SAFE;
|
||||
bool hasZ() const {
|
||||
|
|
|
|||
|
|
@ -385,6 +385,12 @@ class OrderBuildVisitor final : public VNVisitor {
|
|||
iterateLogic(nodep);
|
||||
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
|
||||
nodep->v3fatalSrc("AstFinal should not need ordering");
|
||||
} // LCOV_EXCL_STOP
|
||||
|
|
|
|||
274
src/V3Sched.cpp
274
src/V3Sched.cpp
|
|
@ -192,7 +192,13 @@ LogicClasses gatherLogicClasses(AstNetlist* netlistp) {
|
|||
}
|
||||
} else {
|
||||
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!
|
||||
AstSenTree* createTriggerSenTree(AstNetlist* netlistp, AstVarScope* const vscp, uint32_t index) {
|
||||
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, //
|
||||
AstNode* icoLoop, //
|
||||
const TriggerKit& actTrig, //
|
||||
const EvalKit& actKit, //
|
||||
AstVarScope* preTrigsp, //
|
||||
AstVarScope* nbaTrigsp, //
|
||||
AstCFunc* actFuncp, //
|
||||
AstCFunc* nbaFuncp, //
|
||||
const EvalKit& nbaKit, //
|
||||
const EvalKit& obsKit, //
|
||||
const EvalKit& reactKit, //
|
||||
AstCFunc* postponedFuncp, //
|
||||
TimingKit& timingKit //
|
||||
) {
|
||||
|
|
@ -785,31 +835,16 @@ void createEval(AstNetlist* netlistp, //
|
|||
// Start with the ico loop, if any
|
||||
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
|
||||
AstNodeStmt* const activeEvalLoopp
|
||||
= makeEvalLoop(
|
||||
netlistp, "act", "Active", actTrig.m_vscp, actTrig.m_dumpp,
|
||||
netlistp, "act", "Active", actKit.m_vscp, actKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstNodeStmt* resultp = nullptr;
|
||||
|
||||
// Compute the current triggers
|
||||
{
|
||||
AstCCall* const trigsp = new AstCCall{flp, actTrig.m_funcp};
|
||||
AstCCall* const trigsp = new AstCCall{flp, actKit.m_triggerComputep};
|
||||
trigsp->dtypeSetVoid();
|
||||
resultp = AstNode::addNext(resultp, trigsp->makeStmt());
|
||||
}
|
||||
|
|
@ -822,38 +857,21 @@ void createEval(AstNetlist* netlistp, //
|
|||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
AstNodeStmt* resultp = nullptr;
|
||||
|
||||
// Compute the pre triggers
|
||||
{
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, preTrigsp, VAccess::WRITE};
|
||||
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());
|
||||
}
|
||||
|
||||
AstNodeStmt* resultp
|
||||
= createTriggerAndNotCall(flp, preTrigsp, actKit.m_vscp, nbaKit.m_vscp);
|
||||
// Latch the active trigger flags under the NBA trigger flags
|
||||
{
|
||||
AstVarRef* const lhsp = new AstVarRef{flp, nbaTrigsp, VAccess::WRITE};
|
||||
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());
|
||||
}
|
||||
|
||||
resultp = AstNode::addNext(
|
||||
resultp, createTriggerSetCall(flp, nbaKit.m_vscp, actKit.m_vscp));
|
||||
// Resume triggered timing schedulers
|
||||
if (AstCCall* const resumep = timingKit.createResume(netlistp)) {
|
||||
resultp = AstNode::addNext(resultp, resumep->makeStmt());
|
||||
}
|
||||
|
||||
// Invoke body function
|
||||
{
|
||||
AstCCall* const callp = new AstCCall{flp, actFuncp};
|
||||
AstCCall* const callp = new AstCCall{flp, actKit.m_funcp};
|
||||
callp->dtypeSetVoid();
|
||||
return AstNode::addNext(resultp, callp->makeStmt());
|
||||
resultp = AstNode::addNext(resultp, callp->makeStmt());
|
||||
}
|
||||
|
||||
return resultp;
|
||||
|
|
@ -861,32 +879,75 @@ void createEval(AstNetlist* netlistp, //
|
|||
.second;
|
||||
|
||||
// Create the NBA eval loop. This uses the Active eval loop in the trigger section.
|
||||
AstNodeStmt* const nbaEvalLoopp
|
||||
AstNodeStmt* topEvalLoopp
|
||||
= makeEvalLoop(
|
||||
netlistp, "nba", "NBA", nbaTrigsp, nbaDumpp,
|
||||
netlistp, "nba", "NBA", nbaKit.m_vscp, nbaKit.m_dumpp,
|
||||
[&]() { // Trigger
|
||||
AstNodeStmt* resultp = nullptr;
|
||||
|
||||
// Reset NBA triggers
|
||||
{
|
||||
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());
|
||||
}
|
||||
|
||||
AstNodeStmt* resultp = createTriggerClearCall(flp, nbaKit.m_vscp);
|
||||
// Run the Active eval loop
|
||||
return AstNode::addNext(resultp, activeEvalLoopp);
|
||||
resultp = AstNode::addNext(resultp, activeEvalLoopp);
|
||||
return resultp;
|
||||
},
|
||||
[&]() { // Body
|
||||
AstCCall* const callp = new AstCCall{flp, nbaFuncp};
|
||||
AstCCall* const callp = new AstCCall{flp, nbaKit.m_funcp};
|
||||
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;
|
||||
|
||||
// Add the NBA eval loop
|
||||
funcp->addStmtsp(nbaEvalLoopp);
|
||||
if (obsKit.m_funcp) {
|
||||
// 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
|
||||
if (postponedFuncp) {
|
||||
|
|
@ -989,6 +1050,8 @@ void schedule(AstNetlist* netlistp) {
|
|||
const auto& senTreeps = getSenTreesUsedBy({&logicRegions.m_pre, //
|
||||
&logicRegions.m_act, //
|
||||
&logicRegions.m_nba, //
|
||||
&logicClasses.m_observed, //
|
||||
&logicClasses.m_reactive, //
|
||||
&timingKit.m_lbs});
|
||||
const TriggerKit& actTrig
|
||||
= createTriggers(netlistp, initp, senExprBuilder, senTreeps, "act", extraTriggers);
|
||||
|
|
@ -1002,7 +1065,6 @@ void schedule(AstNetlist* netlistp) {
|
|||
|
||||
AstVarScope* const actTrigVscp = actTrig.m_vscp;
|
||||
AstVarScope* const preTrigVscp = scopeTopp->createTempLike("__VpreTriggered", actTrigVscp);
|
||||
AstVarScope* const nbaTrigVscp = scopeTopp->createTempLike("__VnbaTriggered", actTrigVscp);
|
||||
|
||||
const auto cloneMapWithNewTriggerReferences
|
||||
= [=](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 preTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, preTrigVscp);
|
||||
const auto nbaTrigMap = cloneMapWithNewTriggerReferences(actTrigMap, nbaTrigVscp);
|
||||
if (v3Global.opt.stats()) V3Stats::statsStage("sched-create-triggers");
|
||||
|
||||
// Note: Experiments so far show that running the Act (or Ico) regions on
|
||||
|
|
@ -1061,37 +1122,78 @@ void schedule(AstNetlist* netlistp) {
|
|||
splitCheck(actFuncp);
|
||||
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
|
||||
remapSensitivities(logicRegions.m_nba, nbaTrigMap);
|
||||
remapSensitivities(logicReplicas.m_nba, nbaTrigMap);
|
||||
const auto& nbaTimingDomains = timingKit.remapDomains(nbaTrigMap);
|
||||
// Orders a region's logic and creates the region eval function
|
||||
const auto order = [&](const std::string& name,
|
||||
const std::vector<V3Sched::LogicByScope*>& logic) -> EvalKit {
|
||||
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
|
||||
std::unordered_map<const AstSenItem*, const AstSenTree*> trigToSenNba;
|
||||
invertAndMergeSenTreeMap(trigToSenNba, nbaTrigMap);
|
||||
// Create the inverse map from trigger ref AstSenTree to original AstSenTree
|
||||
std::unordered_map<const AstSenItem*, const AstSenTree*> trigToSen;
|
||||
invertAndMergeSenTreeMap(trigToSen, trigMap);
|
||||
|
||||
AstSenTree* const dpiExportTriggeredNba
|
||||
= createTriggerSenTree(netlistp, nbaTrigVscp, dpiExportTriggerIndex);
|
||||
AstSenTree* const dpiExportTriggered
|
||||
= createTriggerSenTree(netlistp, trigVscp, dpiExportTriggerIndex);
|
||||
|
||||
AstCFunc* const nbaFuncp = V3Order::order(
|
||||
netlistp, {&logicRegions.m_nba, &logicReplicas.m_nba}, trigToSenNba, "nba",
|
||||
v3Global.opt.mtasks(), false, [&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
|
||||
auto it = nbaTimingDomains.find(vscp);
|
||||
if (it != nbaTimingDomains.end()) out = it->second;
|
||||
if (vscp->varp()->isWrittenByDpi()) out.push_back(dpiExportTriggeredNba);
|
||||
const auto& timingDomains = timingKit.remapDomains(trigMap);
|
||||
AstCFunc* const funcp = V3Order::order(
|
||||
netlistp, logic, trigToSen, name, name == "nba" && v3Global.opt.mtasks(), false,
|
||||
[&](const AstVarScope* vscp, std::vector<AstSenTree*>& out) {
|
||||
auto it = timingDomains.find(vscp);
|
||||
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);
|
||||
netlistp->evalNbap(nbaFuncp); // Remember for V3LifePost
|
||||
dumpp->foreach([&](AstText* textp) { //
|
||||
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");
|
||||
|
||||
// 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);
|
||||
|
||||
// Step 12: Bolt it all together to create the '_eval' function
|
||||
createEval(netlistp, icoLoopp, actTrig, preTrigVscp, nbaTrigVscp, actFuncp, nbaFuncp,
|
||||
postponedFuncp, timingKit);
|
||||
// Step 14: Bolt it all together to create the '_eval' function
|
||||
createEval(netlistp, icoLoopp, actKit, preTrigVscp, nbaKit, obsKit, reactKit, postponedFuncp,
|
||||
timingKit);
|
||||
|
||||
transformForks(netlistp);
|
||||
|
||||
|
|
|
|||
|
|
@ -82,6 +82,8 @@ struct LogicClasses final {
|
|||
LogicByScope m_clocked; // Clocked (or sequential) logic (logic with explicit sensitivities)
|
||||
LogicByScope m_hybrid; // Hybrid logic (combinational logic with some explicit sensitivities)
|
||||
LogicByScope m_postponed; // Postponed logic ($strobe)
|
||||
LogicByScope m_observed; // Observed logic (contains AstAlwaysObserved)
|
||||
LogicByScope m_reactive; // Reactive logic (contains AstAlwaysReactive)
|
||||
|
||||
LogicClasses() = default;
|
||||
VL_UNCOPYABLE(LogicClasses);
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ private:
|
|||
// STATE, for passing down one level of hierarchy (may need save/restore)
|
||||
AstCell* m_aboveCellp = nullptr; // Cell that instantiates this module
|
||||
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
|
||||
VarScopeMap m_varScopes; // Varscopes created for each scope and var
|
||||
|
|
@ -247,6 +248,15 @@ private:
|
|||
// We iterate under the *clone*
|
||||
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 {
|
||||
// Add to list of blocks under this scope
|
||||
UINFO(4, " FTASK " << nodep << endl);
|
||||
|
|
@ -265,7 +275,9 @@ private:
|
|||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
// 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;
|
||||
if (AstIfaceRefDType* const ifacerefp = VN_CAST(nodep->dtypep(), IfaceRefDType)) {
|
||||
// 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
|
||||
// assignment under it
|
||||
AstNodeStmt* factorOutTimingControl(AstNodeAssign* nodep) const {
|
||||
AstNodeStmt* stmtp = nodep;
|
||||
AstNode* factorOutTimingControl(AstNodeAssign* nodep) const {
|
||||
AstNode* stmtp = nodep;
|
||||
AstDelay* delayp = getLhsNetDelay(nodep);
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNode* const controlp = nodep->timingControlp();
|
||||
|
|
@ -182,6 +182,11 @@ private:
|
|||
stmtp->replaceWith(eventControlp);
|
||||
eventControlp->addStmtsp(stmtp);
|
||||
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;
|
||||
}
|
||||
|
|
@ -495,6 +500,8 @@ private:
|
|||
m_procp->user2(true);
|
||||
}
|
||||
void visit(AstDelay* nodep) override {
|
||||
UASSERT_OBJ(!nodep->isCycleDelay(), nodep,
|
||||
"Cycle delays should have been handled in V3AssertPre");
|
||||
FileLine* const flp = nodep->fileline();
|
||||
AstNodeExpr* valuep = V3Const::constifyEdit(nodep->lhsp()->unlinkFrBack());
|
||||
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 {
|
||||
if (VN_IS(m_ftaskp, Func)) {
|
||||
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>{
|
||||
/* Generic unsupported warnings */
|
||||
"1step" { FL; return ya1STEP; }
|
||||
"above" { ERROR_RSVD_WORD("AMS"); }
|
||||
"abs" { ERROR_RSVD_WORD("AMS"); }
|
||||
"absdelay" { ERROR_RSVD_WORD("AMS"); }
|
||||
|
|
@ -929,8 +930,7 @@ vnum {vnum1}|{vnum2}|{vnum3}|{vnum4}|{vnum5}
|
|||
return yaTIMENUM;
|
||||
}
|
||||
1step {
|
||||
FL; yylval.cdouble = 0; // Unsupported
|
||||
return yaTIMENUM;
|
||||
return ya1STEP;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
190
src/verilog.y
190
src/verilog.y
|
|
@ -243,6 +243,19 @@ public:
|
|||
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"
|
||||
|
|
@ -494,6 +507,7 @@ BISONPRE_VERSION(3.7,%define api.header.include {"V3ParseBison.h"})
|
|||
// for example yP_ for punctuation based operators.
|
||||
// Double underscores "yX__Y" means token X followed by Y,
|
||||
// and "yX__ETC" means X folled by everything but Y(s).
|
||||
%token<fl> ya1STEP "1step"
|
||||
//UNSUP %token<fl> yACCEPT_ON "accept_on"
|
||||
%token<fl> yALIAS "alias"
|
||||
%token<fl> yALWAYS "always"
|
||||
|
|
@ -2828,13 +2842,13 @@ delay_controlE<delayp>:
|
|||
|
||||
delay_control<delayp>: //== IEEE: delay_control
|
||||
'#' delay_value
|
||||
{ $$ = new AstDelay{$<fl>1, $2}; }
|
||||
{ $$ = new AstDelay{$<fl>1, $2, false}; }
|
||||
| '#' '(' minTypMax ')'
|
||||
{ $$ = new AstDelay{$<fl>1, $3}; }
|
||||
{ $$ = new AstDelay{$<fl>1, $3, false}; }
|
||||
| '#' '(' minTypMax ',' minTypMax ')'
|
||||
{ $$ = new AstDelay{$<fl>1, $3}; RISEFALLDLYUNSUP($3); DEL($5); }
|
||||
{ $$ = new AstDelay{$<fl>1, $3, false}; RISEFALLDLYUNSUP($3); DEL($5); }
|
||||
| '#' '(' 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
|
||||
|
|
@ -3320,6 +3334,10 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
// // IEEE: nonblocking_assignment
|
||||
| fexprLvalue yP_LTE delay_or_event_controlE expr ';'
|
||||
{ $$ = 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 ';'
|
||||
{ $$ = new AstAssign{$1, $2, $5, $4}; }
|
||||
| yDEASSIGN variable_lvalue ';'
|
||||
|
|
@ -3440,8 +3458,14 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
if ($2 && $2->nextp()) nextp = $2->nextp()->unlinkFrBackWithNext();
|
||||
$$ = new AstEventControl{FILELINE_OR_CRE($1), $1, $2};
|
||||
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; }
|
||||
//
|
||||
// // IEEE: wait_statement
|
||||
|
|
@ -3452,13 +3476,6 @@ statement_item<nodep>: // IEEE: statement_item
|
|||
// // IEEE: procedural_assertion_statement
|
||||
| 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; }
|
||||
//
|
||||
// // IEEE: randcase_statement
|
||||
|
|
@ -5403,87 +5420,92 @@ endLabelE<strp>:
|
|||
//************************************************
|
||||
// Clocking
|
||||
|
||||
clocking_declaration<nodep>: // IEEE: clocking_declaration (INCOMPLETE)
|
||||
//UNSUP: vvv remove this -- vastly simplified grammar:
|
||||
yDEFAULT yCLOCKING '@' '(' senitemEdge ')' ';' yENDCLOCKING
|
||||
{ $$ = new AstClocking{$2, $5, nullptr}; }
|
||||
//UNSUP: ^^^ remove this -- vastly simplified grammar:
|
||||
//UNSUP clockingFront clocking_event ';'
|
||||
//UNSUP clocking_itemListE yENDCLOCKING endLabelE { SYMP->popScope($$); }
|
||||
clocking_declaration<nodep>: // IEEE: clocking_declaration
|
||||
yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||
{ $$ = new AstClocking{$<fl>2, *$2, $3, $5, false}; }
|
||||
| yDEFAULT yCLOCKING clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||
{ $$ = new AstClocking{$<fl>2, "", $3, $5, true}; }
|
||||
| yDEFAULT yCLOCKING idAny clocking_event ';' clocking_itemListE yENDCLOCKING endLabelE
|
||||
{ $$ = 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
|
||||
//UNSUP yCLOCKING { PARSEP->symPushNewAnon(VAstType::CLOCKING); }
|
||||
//UNSUP | yCLOCKING idAny/*clocking_identifier*/ { SYMP->pushNew($$); }
|
||||
//UNSUP | yDEFAULT yCLOCKING { PARSEP->symPushNewAnon(VAstType::CLOCKING); }
|
||||
//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 ;
|
||||
clocking_event<senItemp>: // IEEE: clocking_event
|
||||
'@' id
|
||||
{ $$ = new AstSenItem{$<fl>2, VEdgeType::ET_CHANGED, new AstParseRef{$<fl>2, VParseRefExp::PX_TEXT, *$2, nullptr, nullptr}}; }
|
||||
| '@' '(' event_expression ')' { $$ = $3; }
|
||||
;
|
||||
|
||||
//UNSUPclocking_event: // ==IEEE: clocking_event
|
||||
//UNSUP '@' id { }
|
||||
//UNSUP | '@' '(' event_expression ')' { }
|
||||
//UNSUP ;
|
||||
clocking_itemListE<clockingItemp>:
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| clocking_itemList { $$ = $1; }
|
||||
;
|
||||
|
||||
//UNSUPclocking_itemListE:
|
||||
//UNSUP /* empty */ { $$ = nullptr; }
|
||||
//UNSUP | clocking_itemList { $$ = $1; }
|
||||
//UNSUP ;
|
||||
clocking_itemList<clockingItemp>: // IEEE: [ clocking_item ]
|
||||
clocking_item { $$ = $1; }
|
||||
| clocking_itemList clocking_item { if ($1) $$ = addNextNull($1, $2); }
|
||||
;
|
||||
|
||||
//UNSUPclocking_itemList: // IEEE: [ clocking_item ]
|
||||
//UNSUP clocking_item { $$ = $1; }
|
||||
//UNSUP | clocking_itemList clocking_item { $$ = addNextNull($1, $2); }
|
||||
//UNSUP ;
|
||||
clocking_item<clockingItemp>: // IEEE: clocking_item
|
||||
yDEFAULT yINPUT clocking_skew ';' { $$ = new AstClockingItem{$<fl>1, VDirection::INPUT, $3, nullptr}; }
|
||||
| yDEFAULT yOUTPUT clocking_skew ';' { $$ = new AstClockingItem{$<fl>1, VDirection::OUTPUT, $3, nullptr}; }
|
||||
| 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
|
||||
//UNSUP yDEFAULT default_skew ';' { }
|
||||
//UNSUP | clocking_direction list_of_clocking_decl_assign ';' { }
|
||||
//UNSUP | assertion_item_declaration { }
|
||||
//UNSUP ;
|
||||
list_of_clocking_decl_assign<nodep>: // IEEE: list_of_clocking_decl_assign
|
||||
clocking_decl_assign { $$ = $1; }
|
||||
| list_of_clocking_decl_assign ',' clocking_decl_assign
|
||||
{ $$ = addNextNull($1, $3); }
|
||||
;
|
||||
|
||||
//UNSUPdefault_skew: // ==IEEE: default_skew
|
||||
//UNSUP yINPUT clocking_skew { }
|
||||
//UNSUP | yOUTPUT clocking_skew { }
|
||||
//UNSUP | yINPUT clocking_skew yOUTPUT clocking_skew { }
|
||||
//UNSUP ;
|
||||
clocking_decl_assign<nodep>: // IEEE: clocking_decl_assign
|
||||
idAny/*new-signal_identifier*/ exprEqE
|
||||
{ AstParseRef* const refp = new AstParseRef{$<fl>1, VParseRefExp::PX_TEXT, *$1, nullptr, nullptr};
|
||||
$$ = refp;
|
||||
if ($2) $$ = new AstAssign{$<fl>2, refp, $2}; }
|
||||
;
|
||||
|
||||
//UNSUPclocking_direction: // ==IEEE: clocking_direction
|
||||
//UNSUP yINPUT clocking_skewE { }
|
||||
//UNSUP | yOUTPUT clocking_skewE { }
|
||||
//UNSUP | yINPUT clocking_skewE yOUTPUT clocking_skewE { }
|
||||
//UNSUP | yINOUT { }
|
||||
//UNSUP ;
|
||||
clocking_skewE<nodeExprp>: // IEEE: [clocking_skew]
|
||||
/* empty */ { $$ = nullptr; }
|
||||
| clocking_skew { $$ = $1; }
|
||||
;
|
||||
|
||||
//UNSUPlist_of_clocking_decl_assign: // ==IEEE: list_of_clocking_decl_assign
|
||||
//UNSUP clocking_decl_assign { $$ = $1; }
|
||||
//UNSUP | list_of_clocking_decl_assign ',' clocking_decl_assign { }
|
||||
//UNSUP ;
|
||||
clocking_skew<nodeExprp>: // IEEE: clocking_skew
|
||||
delay_control { $$ = $1->lhsp()->unlinkFrBack(); $1->deleteTree(); }
|
||||
| '#' ya1STEP { $$ = new AstConst{$<fl>1, AstConst::OneStep{}}; }
|
||||
| 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
|
||||
//UNSUP idAny/*new-signal_identifier*/ exprEqE { $$ = $1; }
|
||||
//UNSUP ;
|
||||
|
||||
//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 ;
|
||||
cycle_delay<delayp>: // IEEE: cycle_delay
|
||||
yP_POUNDPOUND yaINTNUM { $$ = new AstDelay{$<fl>1, new AstConst{$<fl>2, *$2}, true}; }
|
||||
| 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}; }
|
||||
;
|
||||
|
||||
//************************************************
|
||||
// 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
|
||||
|
||||
default clocking @(posedge clk); endclocking
|
||||
|
||||
assert property ($rose(dly0[0]) || dly0%2==0);
|
||||
|
||||
default clocking @(posedge clk); endclocking
|
||||
assert property ($fell(dly1[0]) || dly1%2==1);
|
||||
|
||||
default clocking @(posedge clk); endclocking
|
||||
assert property ($stable(dly2[31:4]));
|
||||
assert property (!$changed(dly2[31:4]));
|
||||
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