Support cross-module clockvars access (#5184)

This commit is contained in:
Arkadiusz Kozdra 2024-06-30 21:19:02 +02:00 committed by GitHub
parent d6a294b784
commit 72993ec3dd
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 105 additions and 25 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

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

View File

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