mirror of
https://github.com/verilator/verilator.git
synced 2025-01-01 04:07:34 +00:00
Support inout
clocking items (#5160)
This commit is contained in:
parent
3f886f7c61
commit
1dbf1be3e6
@ -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(<sampled var>);
|
||||
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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{
|
||||
|
@ -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()) {
|
||||
|
@ -5918,11 +5918,11 @@ clocking_item<clockingItemp>: // IEEE: clocking_item
|
||||
| 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"); }
|
||||
{ $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::INPUT, $2, $5->cloneTree(true));
|
||||
$$->addNext(GRAMMARP->makeClockingItemList($<fl>3, VDirection::OUTPUT, $4, $5)); }
|
||||
| yINOUT list_of_clocking_decl_assign ';'
|
||||
{ $$ = nullptr;
|
||||
BBUNSUP($1, "Unsupported: 'inout' clocking items"); }
|
||||
{ $$ = GRAMMARP->makeClockingItemList($<fl>1, VDirection::INPUT, nullptr, $2->cloneTree(true));
|
||||
$$->addNext(GRAMMARP->makeClockingItemList($<fl>1, VDirection::OUTPUT, nullptr, $2)); }
|
||||
| assertion_item_declaration
|
||||
{ $$ = nullptr;
|
||||
BBUNSUP($1, "Unsupported: assertion items in clocking blocks"); }
|
||||
|
23
test_regress/t/t_clocking_inout.pl
Executable file
23
test_regress/t/t_clocking_inout.pl
Executable 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;
|
44
test_regress/t/t_clocking_inout.v
Normal file
44
test_regress/t/t_clocking_inout.v
Normal 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
|
@ -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
|
||||
|
@ -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;
|
||||
|
Loading…
Reference in New Issue
Block a user