From 1dbf1be3e60f0cf58065a008a94bcfbbb4976d6c Mon Sep 17 00:00:00 2001 From: Arkadiusz Kozdra Date: Fri, 7 Jun 2024 14:30:58 +0200 Subject: [PATCH] Support `inout` clocking items (#5160) --- src/V3AssertPre.cpp | 14 +++++++-- src/V3AstNodeOther.h | 4 +++ src/V3LinkDot.cpp | 18 ++++++++++++ src/V3LinkLValue.cpp | 4 +++ src/verilog.y | 8 ++--- test_regress/t/t_clocking_inout.pl | 23 +++++++++++++++ test_regress/t/t_clocking_inout.v | 44 ++++++++++++++++++++++++++++ test_regress/t/t_clocking_unsup1.out | 20 +++++-------- test_regress/t/t_clocking_unsup1.v | 4 +-- 9 files changed, 116 insertions(+), 23 deletions(-) create mode 100755 test_regress/t/t_clocking_inout.pl create mode 100644 test_regress/t/t_clocking_inout.v diff --git a/src/V3AssertPre.cpp b/src/V3AssertPre.cpp index 878e77d2c..19492cbe8 100644 --- a/src/V3AssertPre.cpp +++ b/src/V3AssertPre.cpp @@ -158,6 +158,13 @@ private: iterateChildren(nodep); } 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(); V3Const::constifyEdit(nodep->skewp()); if (!VN_IS(nodep->skewp(), Const)) { @@ -168,9 +175,7 @@ private: 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); + m_clockingp->addVarsp(varp->unlinkFrBack()); varp->user1p(nodep); if (nodep->direction() == VDirection::OUTPUT) { AstVarRef* const skewedRefp = new AstVarRef{flp, varp, VAccess::READ}; @@ -224,6 +229,7 @@ private: AstVar* const queueVarp = new AstVar{ flp, VVarType::MODULETEMP, "__Vqueue__" + m_clockingp->name() + "__DOT__" + varp->name(), queueDtp}; + queueVarp->lifetime(VLifetime::STATIC); m_clockingp->addNextHere(queueVarp); // Create a process like this: // always queue.push(); @@ -290,6 +296,8 @@ private: const std::string delayName = m_cycleDlyNames.get(nodep); AstVar* const cntVarp = new AstVar{flp, VVarType::BLOCKTEMP, delayName + "__counter", nodep->findBasicDType(VBasicDTypeKwd::UINT32)}; + cntVarp->lifetime(VLifetime::AUTOMATIC); + cntVarp->funcLocal(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 AstWhile{ diff --git a/src/V3AstNodeOther.h b/src/V3AstNodeOther.h index e114cf2d5..1fbd52caf 100644 --- a/src/V3AstNodeOther.h +++ b/src/V3AstNodeOther.h @@ -956,6 +956,7 @@ class AstClockingItem final : public AstNode { // @astgen op2 := exprp : Optional[AstNodeExpr] // @astgen op3 := assignp : Optional[AstAssign] // @astgen op4 := varp : Optional[AstVar] + // @astgen ptr := m_outputp : Optional[AstClockingItem] VDirection m_direction; public: @@ -971,6 +972,9 @@ public: } ASTGEN_MEMBERS_AstClockingItem; 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 { // Container for const static data diff --git a/src/V3LinkDot.cpp b/src/V3LinkDot.cpp index f2e185a48..d6bbb672e 100644 --- a/src/V3LinkDot.cpp +++ b/src/V3LinkDot.cpp @@ -1211,6 +1211,23 @@ class LinkDotFindVisitor final : public VNVisitor { foundp = m_modSymp; // Conflicts with modname? } 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; if (!foundp) { ins = true; @@ -2181,6 +2198,7 @@ class LinkDotResolveVisitor final : public VNVisitor { AstVar* const eventp = new AstVar{ clockingp->fileline(), VVarType::MODULETEMP, clockingp->name(), VFlagChildDType{}, new AstBasicDType{clockingp->fileline(), VBasicDTypeKwd::EVENT}}; + eventp->lifetime(VLifetime::STATIC); clockingp->eventp(eventp); // Trigger the clocking event in Observed (IEEE 1800-2023 14.13) clockingp->addNextHere(new AstAlwaysObserved{ diff --git a/src/V3LinkLValue.cpp b/src/V3LinkLValue.cpp index d9c2c53e7..72a533183 100644 --- a/src/V3LinkLValue.cpp +++ b/src/V3LinkLValue.cpp @@ -50,6 +50,10 @@ class LinkLValueVisitor final : public VNVisitor { // so it is needed to check only if m_setContinuously is 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) { nodep->varp()->setForcedByCode(); } else if (!nodep->varp()->isFuncLocal() && nodep->varp()->isReadOnly()) { diff --git a/src/verilog.y b/src/verilog.y index f73ec9104..e35565adb 100644 --- a/src/verilog.y +++ b/src/verilog.y @@ -5918,11 +5918,11 @@ clocking_item: // IEEE: clocking_item | yOUTPUT clocking_skewE list_of_clocking_decl_assign ';' { $$ = GRAMMARP->makeClockingItemList($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"); } + { $$ = GRAMMARP->makeClockingItemList($1, VDirection::INPUT, $2, $5->cloneTree(true)); + $$->addNext(GRAMMARP->makeClockingItemList($3, VDirection::OUTPUT, $4, $5)); } | yINOUT list_of_clocking_decl_assign ';' - { $$ = nullptr; - BBUNSUP($1, "Unsupported: 'inout' clocking items"); } + { $$ = GRAMMARP->makeClockingItemList($1, VDirection::INPUT, nullptr, $2->cloneTree(true)); + $$->addNext(GRAMMARP->makeClockingItemList($1, VDirection::OUTPUT, nullptr, $2)); } | assertion_item_declaration { $$ = nullptr; BBUNSUP($1, "Unsupported: assertion items in clocking blocks"); } diff --git a/test_regress/t/t_clocking_inout.pl b/test_regress/t/t_clocking_inout.pl new file mode 100755 index 000000000..b8493bd06 --- /dev/null +++ b/test_regress/t/t_clocking_inout.pl @@ -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; diff --git a/test_regress/t/t_clocking_inout.v b/test_regress/t/t_clocking_inout.v new file mode 100644 index 000000000..885c26e0a --- /dev/null +++ b/test_regress/t/t_clocking_inout.v @@ -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 diff --git a/test_regress/t/t_clocking_unsup1.out b/test_regress/t/t_clocking_unsup1.out index 164271f81..9904a7a65 100644 --- a/test_regress/t/t_clocking_unsup1.out +++ b/test_regress/t/t_clocking_unsup1.out @@ -1,17 +1,11 @@ -%Error-UNSUPPORTED: t/t_clocking_unsup1.v:14:26: Unsupported: Mixed input/output clocking items - 14 | input #1 output #1step x; - | ^ +%Error-UNSUPPORTED: t/t_clocking_unsup1.v:14:15: Unsupported: clocking event edge override + 14 | output posedge #1 a; + | ^~~~~~~ ... 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:15:15: Unsupported: clocking event edge override + 15 | output negedge #1 b; + | ^~~~~~~ %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; + 16 | output edge #1 b; | ^~~~ %Error: Exiting due to diff --git a/test_regress/t/t_clocking_unsup1.v b/test_regress/t/t_clocking_unsup1.v index 7315e2e63..a15a9212d 100644 --- a/test_regress/t/t_clocking_unsup1.v +++ b/test_regress/t/t_clocking_unsup1.v @@ -10,9 +10,7 @@ module t(/*AUTOARG*/ ); input clk; - global clocking cb @(posedge clk); - input #1 output #1step x; - inout y; + clocking cb @(posedge clk); output posedge #1 a; output negedge #1 b; output edge #1 b;