mirror of
https://github.com/verilator/verilator.git
synced 2025-04-06 12:42:42 +00:00
Support inout
clocking items (#5160)
This commit is contained in:
parent
3f886f7c61
commit
1dbf1be3e6
@ -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{
|
||||||
|
@ -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
|
||||||
|
@ -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{
|
||||||
|
@ -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()) {
|
||||||
|
@ -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"); }
|
||||||
|
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
|
%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
|
||||||
|
@ -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;
|
||||||
|
Loading…
Reference in New Issue
Block a user