Support cross-module clockvars access (#5184)
This commit is contained in:
parent
d6a294b784
commit
72993ec3dd
|
|
@ -57,6 +57,7 @@ private:
|
|||
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
|
||||
std::vector<AstVarXRef*> m_xrefsp; // list of xrefs that need name fixup
|
||||
|
||||
// METHODS
|
||||
|
||||
|
|
@ -156,6 +157,8 @@ private:
|
|||
m_clockingp = nodep;
|
||||
UINFO(8, " CLOCKING" << nodep << endl);
|
||||
iterateChildren(nodep);
|
||||
if (nodep->eventp()) nodep->addNextHere(nodep->eventp()->unlinkFrBack());
|
||||
VL_DO_DANGLING(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstClockingItem* const nodep) override {
|
||||
// Get a ref to the sampled/driven variable
|
||||
|
|
@ -175,7 +178,8 @@ private:
|
|||
AstConst* const skewp = VN_AS(nodep->skewp(), Const);
|
||||
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
|
||||
AstNodeExpr* const exprp = nodep->exprp();
|
||||
m_clockingp->addVarsp(varp->unlinkFrBack());
|
||||
varp->name(m_clockingp->name() + "__DOT__" + varp->name());
|
||||
m_clockingp->addNextHere(varp->unlinkFrBack());
|
||||
varp->user1p(nodep);
|
||||
if (nodep->direction() == VDirection::OUTPUT) {
|
||||
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
|
||||
|
|
@ -226,9 +230,8 @@ private:
|
|||
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};
|
||||
AstVar* const queueVarp
|
||||
= new AstVar{flp, VVarType::MODULETEMP, "__Vqueue__" + varp->name(), queueDtp};
|
||||
queueVarp->lifetime(VLifetime::STATIC);
|
||||
m_clockingp->addNextHere(queueVarp);
|
||||
// Create a process like this:
|
||||
|
|
@ -241,7 +244,7 @@ private:
|
|||
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>});
|
||||
// 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()}};
|
||||
|
|
@ -265,6 +268,7 @@ private:
|
|||
nodep->v3error("Only cycle delays can be used in synchronous drives"
|
||||
" (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
iterateChildren(nodep);
|
||||
return;
|
||||
}
|
||||
if (m_inAssign && !m_inSynchDrive) {
|
||||
|
|
@ -317,9 +321,26 @@ private:
|
|||
}
|
||||
}
|
||||
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) {
|
||||
UINFO(8, " -varref: " << nodep << endl);
|
||||
UINFO(8, " -varref-var-back: " << nodep->varp()->backp() << endl);
|
||||
UINFO(8, " -varref-var-user1: " << nodep->varp()->user1p() << endl);
|
||||
if (AstClockingItem* const itemp = VN_CAST(
|
||||
nodep->varp()->user1p() ? nodep->varp()->user1p() : nodep->varp()->firstAbovep(),
|
||||
ClockingItem)) {
|
||||
if (nodep->user1()) return;
|
||||
|
||||
// ensure linking still works, this has to be done only once
|
||||
if (AstVarXRef* xrefp = VN_CAST(nodep, VarXRef)) {
|
||||
UINFO(8, " -clockvarxref-in: " << xrefp << endl);
|
||||
string dotted = xrefp->dotted();
|
||||
const size_t dotPos = dotted.rfind('.');
|
||||
dotted.erase(dotPos, string::npos);
|
||||
xrefp->dotted(dotted);
|
||||
UINFO(8, " -clockvarxref-out: " << xrefp << endl);
|
||||
m_xrefsp.emplace_back(xrefp);
|
||||
}
|
||||
|
||||
if (nodep->access().isReadOrRW() && itemp->direction() == VDirection::OUTPUT) {
|
||||
nodep->v3error("Cannot read from output clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
if (nodep->access().isWriteOrRW()) {
|
||||
|
|
@ -329,10 +350,11 @@ private:
|
|||
"to clockvars (IEEE 1800-2023 14.16)");
|
||||
}
|
||||
if (m_inAssign) m_inSynchDrive = true;
|
||||
} else if (!nodep->user1() && itemp->direction() == VDirection::INPUT) {
|
||||
} else if (itemp->direction() == VDirection::INPUT) {
|
||||
nodep->v3error("Cannot write to input clockvar (IEEE 1800-2023 14.3)");
|
||||
}
|
||||
}
|
||||
nodep->user1(true);
|
||||
}
|
||||
}
|
||||
void visit(AstNodeAssign* nodep) override {
|
||||
|
|
@ -496,6 +518,8 @@ public:
|
|||
clearAssertInfo();
|
||||
// Process
|
||||
iterate(nodep);
|
||||
// Fix up varref names
|
||||
for (AstVarXRef* xrefp : m_xrefsp) xrefp->name(xrefp->varp()->name());
|
||||
}
|
||||
~AssertPreVisitor() override = default;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -5645,6 +5645,7 @@ public:
|
|||
inline AstVarXRef(FileLine* fl, AstVar* varp, const string& dotted, const VAccess& access);
|
||||
ASTGEN_MEMBERS_AstVarXRef;
|
||||
string name() const override VL_MT_STABLE { return m_name; } // * = Var name
|
||||
void name(const std::string& name) override { m_name = name; } // * = Var name
|
||||
void dump(std::ostream& str) const override;
|
||||
void dumpJson(std::ostream& str) const override;
|
||||
string dotted() const { return m_dotted; }
|
||||
|
|
|
|||
|
|
@ -926,8 +926,7 @@ class AstClocking final : public 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]
|
||||
// @astgen op3 := eventp : Optional[AstVar]
|
||||
std::string m_name; // Clocking block name
|
||||
const bool m_isDefault = false; // True if default clocking
|
||||
const bool m_isGlobal = false; // True if global clocking
|
||||
|
|
|
|||
|
|
@ -95,7 +95,6 @@ class ClockVisitor final : public VNVisitor {
|
|||
= string{"__Vsampled__"} + vscp->scopep()->nameDotless() + "__" + varp->name();
|
||||
FileLine* const flp = vscp->fileline();
|
||||
AstVar* const newvarp = new AstVar{flp, VVarType::MODULETEMP, newvarname, varp->dtypep()};
|
||||
newvarp->noReset(true); // Reset by below assign
|
||||
m_scopep->modp()->addStmtsp(newvarp);
|
||||
AstVarScope* const newvscp = new AstVarScope{flp, m_scopep, newvarp};
|
||||
vscp->user1p(newvscp);
|
||||
|
|
|
|||
|
|
@ -2875,6 +2875,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
|
|||
}
|
||||
} else if (VN_IS(foundp->nodep(), Clocking)) {
|
||||
m_ds.m_dotSymp = foundp;
|
||||
if (m_ds.m_dotText != "") m_ds.m_dotText += "." + nodep->name();
|
||||
ok = m_ds.m_dotPos == DP_SCOPE;
|
||||
} else if (const AstNodeFTask* const ftaskp = VN_CAST(foundp->nodep(), NodeFTask)) {
|
||||
|
||||
|
|
|
|||
|
|
@ -51,7 +51,6 @@ class ScopeVisitor final : public VNVisitor {
|
|||
// 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
|
||||
|
|
@ -243,15 +242,6 @@ class ScopeVisitor final : public VNVisitor {
|
|||
// 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(pushDeletep(nodep->unlinkFrBack()), nodep);
|
||||
}
|
||||
void visit(AstNodeFTask* nodep) override {
|
||||
// Add to list of blocks under this scope
|
||||
UINFO(4, " FTASK " << nodep << endl);
|
||||
|
|
@ -270,9 +260,7 @@ class ScopeVisitor final : public VNVisitor {
|
|||
}
|
||||
void visit(AstVar* nodep) override {
|
||||
// Make new scope variable
|
||||
if (m_clockingp) {
|
||||
nodep->name(VString::dot(m_clockingp->name(), "__DOT__", nodep->name()));
|
||||
} else if (!nodep->user1p()) {
|
||||
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
|
||||
|
|
|
|||
|
|
@ -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 Wilson Snyder. This program is free software; you
|
||||
# can redistribute it and/or modify it under the terms of either the GNU
|
||||
# Lesser General Public License Version 3 or the Perl Artistic License
|
||||
# Version 2.0.
|
||||
# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0
|
||||
|
||||
scenarios(simulator => 1);
|
||||
|
||||
compile(
|
||||
verilator_flags2 => ["--exe --main --timing"],
|
||||
);
|
||||
|
||||
execute(
|
||||
check_finished => 1,
|
||||
);
|
||||
|
||||
ok(1);
|
||||
1;
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain, for
|
||||
// any use, without warranty, 2024 by Antmicro Ltd.
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
module mod;
|
||||
logic clk = 1'b0;
|
||||
logic inp = 1'b0;
|
||||
clocking cb @(posedge clk);
|
||||
input #3 inp;
|
||||
endclocking
|
||||
always @(posedge clk) inp <= 1'b1;
|
||||
always #1 clk = ~clk;
|
||||
endmodule
|
||||
|
||||
module main;
|
||||
logic clk = 1'b0;
|
||||
logic inp = 1'b0;
|
||||
always begin
|
||||
#2
|
||||
if (t.mod1.cb.inp != 1'b0) $stop;
|
||||
if (t.main1.cbb.inp != 1'b0) $stop;
|
||||
if (t.main2.cbb.inp != 1'b0) $stop;
|
||||
#4;
|
||||
if (t.mod1.cb.inp != 1'b1) $stop;
|
||||
if (t.main1.cbb.inp != 1'b1) $stop;
|
||||
if (t.main2.cbb.inp != 1'b1) $stop;
|
||||
end
|
||||
clocking cbb @(posedge clk);
|
||||
input #3 inp;
|
||||
endclocking
|
||||
always @(posedge clk) inp <= 1'b1;
|
||||
always #1 clk = ~clk;
|
||||
endmodule
|
||||
|
||||
module t;
|
||||
main main1();
|
||||
mod mod1();
|
||||
main main2();
|
||||
initial begin
|
||||
#7;
|
||||
$write("*-* All Finished *-*\n");
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
Loading…
Reference in New Issue