Support inout clocking items (#5160)

This commit is contained in:
Arkadiusz Kozdra 2024-06-07 14:30:58 +02:00 committed by GitHub
parent 3f886f7c61
commit 1dbf1be3e6
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 116 additions and 23 deletions

View File

@ -158,6 +158,13 @@ private:
iterateChildren(nodep); iterateChildren(nodep);
} }
void visit(AstClockingItem* const nodep) override { void visit(AstClockingItem* const nodep) override {
// Get a ref to the sampled/driven variable
AstVar* const varp = nodep->varp();
if (!varp) {
// Unused item
pushDeletep(nodep->unlinkFrBack());
return;
}
FileLine* const flp = nodep->fileline(); FileLine* const flp = nodep->fileline();
V3Const::constifyEdit(nodep->skewp()); V3Const::constifyEdit(nodep->skewp());
if (!VN_IS(nodep->skewp(), Const)) { if (!VN_IS(nodep->skewp(), Const)) {
@ -168,9 +175,7 @@ private:
AstConst* const skewp = VN_AS(nodep->skewp(), Const); AstConst* const skewp = VN_AS(nodep->skewp(), Const);
if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative"); if (skewp->num().isNegative()) skewp->v3error("Skew cannot be negative");
AstNodeExpr* const exprp = nodep->exprp(); AstNodeExpr* const exprp = nodep->exprp();
// Get a ref to the sampled/driven variable m_clockingp->addVarsp(varp->unlinkFrBack());
AstVar* const varp = nodep->varp()->unlinkFrBack();
m_clockingp->addVarsp(varp);
varp->user1p(nodep); varp->user1p(nodep);
if (nodep->direction() == VDirection::OUTPUT) { if (nodep->direction() == VDirection::OUTPUT) {
AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ}; AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ};
@ -224,6 +229,7 @@ private:
AstVar* const queueVarp = new AstVar{ AstVar* const queueVarp = new AstVar{
flp, VVarType::MODULETEMP, flp, VVarType::MODULETEMP,
"__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp}; "__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp};
queueVarp->lifetime(VLifetime::STATIC);
m_clockingp->addNextHere(queueVarp); m_clockingp->addNextHere(queueVarp);
// Create a process like this: // Create a process like this:
// always queue.push(<sampled var>); // always queue.push(<sampled var>);
@ -290,6 +296,8 @@ private:
const std::string delayName = m_cycleDlyNames.get(nodep); const std::string delayName = m_cycleDlyNames.get(nodep);
AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter", AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter",
nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; nodep->findBasicDType(VBasicDTypeKwd::UINT32)};
cntVarp->lifetime(VLifetime::AUTOMATIC);
cntVarp->funcLocal(true);
AstBegin* const beginp = new AstBegin{flp, delayName + "__block", cntVarp, false, true}; 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 AstAssign{flp, new AstVarRef{flp, cntVarp, VAccess::WRITE}, valuep});
beginp->addStmtsp(new AstWhile{ beginp->addStmtsp(new AstWhile{

View File

@ -956,6 +956,7 @@ class AstClockingItem final : public AstNode {
// @astgen op2 := exprp : Optional[AstNodeExpr] // @astgen op2 := exprp : Optional[AstNodeExpr]
// @astgen op3 := assignp : Optional[AstAssign] // @astgen op3 := assignp : Optional[AstAssign]
// @astgen op4 := varp : Optional[AstVar] // @astgen op4 := varp : Optional[AstVar]
// @astgen ptr := m_outputp : Optional[AstClockingItem]
VDirection m_direction; VDirection m_direction;
public: public:
@ -971,6 +972,9 @@ public:
} }
ASTGEN_MEMBERS_AstClockingItem; ASTGEN_MEMBERS_AstClockingItem;
VDirection direction() const { return m_direction; } VDirection direction() const { return m_direction; }
AstClockingItem* outputp() const { return m_outputp; }
void outputp(AstClockingItem* outputp) { m_outputp = outputp; }
bool maybePointedTo() const override { return true; }
}; };
class AstConstPool final : public AstNode { class AstConstPool final : public AstNode {
// Container for const static data // Container for const static data

View File

@ -1211,6 +1211,23 @@ class LinkDotFindVisitor final : public VNVisitor {
foundp = m_modSymp; // Conflicts with modname? foundp = m_modSymp; // Conflicts with modname?
} }
AstVar* const findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : nullptr; AstVar* const findvarp = foundp ? VN_CAST(foundp->nodep(), Var) : nullptr;
// clocking items can have duplicate names (inout)
if (findvarp && VN_IS(findvarp->backp(), ClockingItem)
&& VN_IS(nodep->backp(), ClockingItem)) {
AstClockingItem* const itemp = VN_AS(nodep->backp(), ClockingItem);
AstClockingItem* const finditemp = VN_AS(findvarp->backp(), ClockingItem);
UINFO(4, "ClockCompl: " << itemp << " ;; " << finditemp << endl);
UINFO(4, "ClockCompV: " << nodep << " ;; " << findvarp << endl);
if (*itemp->exprp()->fileline() == *finditemp->exprp()->fileline()) {
UASSERT_OBJ(finditemp->direction() == VDirection::INPUT
&& itemp->direction() == VDirection::OUTPUT,
itemp, "Input after output?");
// pretend nothing found and rename
foundp = nullptr;
nodep->name("__Voutput_" + nodep->name());
finditemp->outputp(itemp);
}
}
bool ins = false; bool ins = false;
if (!foundp) { if (!foundp) {
ins = true; ins = true;
@ -2181,6 +2198,7 @@ class LinkDotResolveVisitor final : public VNVisitor {
AstVar* const eventp = new AstVar{ AstVar* const eventp = new AstVar{
clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{}, clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{},
new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}}; new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}};
eventp->lifetime(VLifetime::STATIC);
clockingp->eventp(eventp); clockingp->eventp(eventp);
// Trigger the clocking event in Observed (IEEE 1800-2023 14.13) // Trigger the clocking event in Observed (IEEE 1800-2023 14.13)
clockingp->addNextHere(new AstAlwaysObserved{ clockingp->addNextHere(new AstAlwaysObserved{

View File

@ -50,6 +50,10 @@ class LinkLValueVisitor final : public VNVisitor {
// so it is needed to check only if m_setContinuously is true // so it is needed to check only if m_setContinuously is true
if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true); if (m_setStrengthSpecified) nodep->varp()->hasStrengthAssignment(true);
} }
if (const AstClockingItem* itemp = VN_CAST(nodep->varp()->backp(), ClockingItem)) {
UINFO(5, "ClkOut " << nodep << endl);
if (itemp->outputp()) nodep->varp(itemp->outputp()->varp());
}
if (m_setForcedByCode) { if (m_setForcedByCode) {
nodep->varp()->setForcedByCode(); nodep->varp()->setForcedByCode();
} else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) { } else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) {

View File

@ -5918,11 +5918,11 @@ clocking_item<clockingItemp>: // IEEE: clocking_item
| yOUTPUT clocking_skewE list_of_clocking_decl_assign ';' | yOUTPUT clocking_skewE list_of_clocking_decl_assign ';'
{ $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::OUTPUT, $2, $3); } { $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::OUTPUT, $2, $3); }
| yINPUT clocking_skewE yOUTPUT clocking_skewE list_of_clocking_decl_assign ';' | yINPUT clocking_skewE yOUTPUT clocking_skewE list_of_clocking_decl_assign ';'
{ $$ = nullptr; { $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::INPUT, $2, $5->cloneTree(true));
BBUNSUP($5, "Unsupported: Mixed input/output clocking items"); } $$->addNext(GRAMMARP->makeClockingItemList($<fl>3, VDirection::OUTPUT, $4, $5)); }
| yINOUT list_of_clocking_decl_assign ';' | yINOUT list_of_clocking_decl_assign ';'
{ $$ = nullptr; { $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::INPUT, nullptr, $2->cloneTree(true));
BBUNSUP($1, "Unsupported: 'inout' clocking items"); } $$->addNext(GRAMMARP->makeClockingItemList($<fl>1, VDirection::OUTPUT, nullptr, $2)); }
| assertion_item_declaration | assertion_item_declaration
{ $$ = nullptr; { $$ = nullptr;
BBUNSUP($1, "Unsupported: assertion items in clocking blocks"); } BBUNSUP($1, "Unsupported: assertion items in clocking blocks"); }

View File

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

View File

@ -0,0 +1,44 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2023 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t;
logic clk = 0, foo = 0, bar = 0;
always #5 clk = ~clk;
clocking cb @(posedge clk);
input #11 output #2 foo;
inout bar;
endclocking
initial begin
cb.foo <= 1;
cb.bar <= 1;
if (foo != 0 || cb.foo != 0) $stop;
if (bar != 0 || cb.bar != 0) $stop;
@(posedge bar)
if ($time != 5) $stop;
if (foo != 0 || cb.foo != 0) $stop;
if (cb.bar != 0) $stop;
#1
if (foo != 0 || cb.foo != 0) $stop;
if (cb.bar != 1) $stop;
@(posedge foo)
if ($time != 7) $stop;
if (cb.foo != 0) $stop;
#9 // $time == 16
if (cb.foo != 0) $stop;
#10 // $time == 26
if (cb.foo != 1) $stop;
$write("*-* All Finished *-*\n");
$finish;
end
endmodule

View File

@ -1,17 +1,11 @@
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:14:26: Unsupported: Mixed input/output clocking items %Error-UNSUPPORTED: t/t_clocking_unsup1.v:14:15: Unsupported: clocking event edge override
14 | input #1 output #1step x; 14 | output posedge #1 a;
| ^ | ^~~~~~~
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest ... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:15:8: Unsupported: 'inout' clocking items %Error-UNSUPPORTED: t/t_clocking_unsup1.v:15:15: Unsupported: clocking event edge override
15 | inout y; 15 | output negedge #1 b;
| ^~~~~ | ^~~~~~~
%Error-UNSUPPORTED: t/t_clocking_unsup1.v:16:15: Unsupported: clocking event edge override %Error-UNSUPPORTED: t/t_clocking_unsup1.v:16:15: Unsupported: clocking event edge override
16 | output posedge #1 a; 16 | output edge #1 b;
| ^~~~~~~
%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: Exiting due to %Error: Exiting due to

View File

@ -10,9 +10,7 @@ module t(/*AUTOARG*/
); );
input clk; input clk;
global clocking cb @(posedge clk); clocking cb @(posedge clk);
input #1 output #1step x;
inout y;
output posedge #1 a; output posedge #1 a;
output negedge #1 b; output negedge #1 b;
output edge #1 b; output edge #1 b;