Support clocking blocks (#3674)

This commit is contained in:
Krzysztof Bieganski 2022-12-23 12:34:49 +00:00 committed by GitHub
parent d64971ba35
commit bb44d4e4f2
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
58 changed files with 1817 additions and 218 deletions

View File

@ -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, ...) \

View File

@ -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);
}

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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]";
}

View File

@ -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);
}

View File

@ -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);

View File

@ -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
}

View File

@ -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.");

View File

@ -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

View File

@ -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) {

View File

@ -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

View File

@ -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)) {

View File

@ -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 {

View File

@ -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

View File

@ -192,8 +192,14 @@ LogicClasses gatherLogicClasses(AstNetlist* netlistp) {
}
} else {
UASSERT_OBJ(senTreep->hasClocked(), activep, "What else could it be?");
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);
}
}
});
for (AstActive* const activep : empty) activep->unlinkFrBack()->deleteTree();
@ -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();
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;
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;
// Add the NBA eval loop
funcp->addStmtsp(nbaEvalLoopp);
}
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};
// 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
remapSensitivities(logicRegions.m_nba, nbaTrigMap);
remapSensitivities(logicReplicas.m_nba, nbaTrigMap);
const auto& nbaTimingDomains = timingKit.remapDomains(nbaTrigMap);
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);
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);
});
splitCheck(nbaFuncp);
netlistp->evalNbap(nbaFuncp); // Remember for V3LifePost
// 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});
}
});
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);

View File

@ -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);

View File

@ -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

View File

@ -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);

View File

@ -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 "

View File

@ -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;
}
}

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

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

View File

@ -0,0 +1,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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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 *-*

View File

@ -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;

View File

@ -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;

View File

@ -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 *-*

View File

@ -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;

View File

@ -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

View File

@ -0,0 +1,30 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 by 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;

View File

@ -0,0 +1,30 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2022 by 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;

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,20 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 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;

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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;