Add handling of tristate select/extend (#3604)

This commit is contained in:
Ryszard Rozak 2022-10-01 16:34:30 +02:00 committed by GitHub
parent 4db998d357
commit 46b8dca360
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 149 additions and 17 deletions

View File

@ -493,6 +493,47 @@ class TristateVisitor final : public TristateBaseVisitor {
}
return VN_AS(invarp->user1p(), Var);
}
AstConst* getNonZConstp(AstConst* const constp) {
FileLine* const fl = constp->fileline();
V3Number numz{constp, constp->width()};
numz.opBitsZ(constp->num()); // Z->1, else 0
V3Number numz0{constp, constp->width()};
numz0.opNot(numz); // Z->0, else 1
return new AstConst{fl, numz0};
}
AstNode* getEnExprBasedOnOriginalp(AstNode* const nodep) {
if (AstVarRef* const varrefp = VN_CAST(nodep, VarRef)) {
return new AstVarRef{varrefp->fileline(), getCreateEnVarp(varrefp->varp()),
VAccess::READ};
} else if (AstConst* const constp = VN_CAST(nodep, Const)) {
return getNonZConstp(constp);
} else if (AstExtend* const extendp = VN_CAST(nodep, Extend)) {
// Extend inserts 0 at the beginning. 0 in __en variable means that this bit equals z,
// so in order to preserve the value of the original AstExtend node we should insert 1
// instead of 0. To extend __en expression we have to negate its lhsp() and then negate
// whole extend.
// Unlink lhsp before copying to save unnecessary copy of lhsp
AstNode* const lhsp = extendp->lhsp()->unlinkFrBack();
AstExtend* const enExtendp = extendp->cloneTree(false);
extendp->lhsp(lhsp);
AstNode* const enLhsp = getEnExprBasedOnOriginalp(lhsp);
enExtendp->lhsp(new AstNot{enLhsp->fileline(), enLhsp});
return new AstNot{enExtendp->fileline(), enExtendp};
} else if (AstSel* const selp = VN_CAST(nodep, Sel)) {
AstNode* const fromp = selp->fromp()->unlinkFrBack();
AstSel* const enSelp = selp->cloneTree(false);
selp->fromp(fromp);
AstNode* const enFromp = getEnExprBasedOnOriginalp(fromp);
enSelp->fromp(enFromp);
return enSelp;
} else {
nodep->v3warn(E_UNSUPPORTED,
"Unsupported tristate construct: " << nodep->prettyTypeName()
<< " in function " << __func__);
return nullptr;
}
}
AstVar* getCreateOutVarp(AstVar* invarp) {
// Return the master __out for the specified input variable
if (!invarp->user4p()) {
@ -959,14 +1000,10 @@ class TristateVisitor final : public TristateBaseVisitor {
} else if (m_tgraph.isTristate(nodep)) {
m_tgraph.didProcess(nodep);
FileLine* const fl = nodep->fileline();
V3Number numz(nodep, nodep->width());
numz.opBitsZ(nodep->num()); // Z->1, else 0
V3Number numz0(nodep, nodep->width());
numz0.opNot(numz); // Z->0, else 1
V3Number num1(nodep, nodep->width());
num1.opAnd(nodep->num(), numz0); // 01X->01X, Z->0
AstConst* const newconstp = new AstConst(fl, num1);
AstConst* const enp = new AstConst(fl, numz0);
AstConst* const enp = getNonZConstp(nodep);
V3Number num1{nodep, nodep->width()};
num1.opAnd(nodep->num(), enp->num()); // 01X->01X, Z->0
AstConst* const newconstp = new AstConst{fl, num1};
nodep->replaceWith(newconstp);
VL_DO_DANGLING(pushDeletep(nodep), nodep);
newconstp->user1p(enp); // Propagate up constant with non-Z bits as 1
@ -1263,23 +1300,27 @@ class TristateVisitor final : public TristateBaseVisitor {
UINFO(9, dbgState() << nodep << endl);
// Constification always moves const to LHS
AstConst* const constp = VN_CAST(nodep->lhsp(), Const);
AstVarRef* const varrefp = VN_CAST(nodep->rhsp(), VarRef); // Input variable
if (constp && constp->user1p() && varrefp) {
if (constp && constp->user1p()) {
// 3'b1z0 -> ((3'b101 == in__en) && (3'b100 == in))
varrefp->unlinkFrBack();
AstNode* const rhsp = nodep->rhsp();
rhsp->unlinkFrBack();
FileLine* const fl = nodep->fileline();
AstNode* enRhsp;
if (rhsp->user1p()) {
enRhsp = rhsp->user1p();
rhsp->user1p(nullptr);
} else {
enRhsp = getEnExprBasedOnOriginalp(rhsp);
}
const V3Number oneIfEn
= VN_AS(constp->user1p(), Const)
->num(); // visit(AstConst) already split into en/ones
const V3Number& oneIfEnOne = constp->num();
AstVar* const envarp = getCreateEnVarp(varrefp->varp());
AstNode* newp
= new AstLogAnd(fl,
new AstEq(fl, new AstConst(fl, oneIfEn),
new AstVarRef(fl, envarp, VAccess::READ)),
= new AstLogAnd{fl, new AstEq{fl, new AstConst{fl, oneIfEn}, enRhsp},
// Keep the caseeq if there are X's present
new AstEqCase(fl, new AstConst(fl, oneIfEnOne), varrefp));
if (neq) newp = new AstLogNot(fl, newp);
new AstEqCase{fl, new AstConst{fl, oneIfEnOne}, rhsp}};
if (neq) newp = new AstLogNot{fl, newp};
UINFO(9, " newceq " << newp << endl);
if (debug() >= 9) nodep->dumpTree(cout, "-caseeq-old: ");
if (debug() >= 9) newp->dumpTree(cout, "-caseeq-new: ");

View File

@ -0,0 +1,8 @@
%Error-UNSUPPORTED: t/t_tri_and_eqcase.v:9:28: Unsupported tristate construct: AND in function getEnExprBasedOnOriginalp
9 | logic b = 1'z === (clk1 & clk2);
| ^
... For error description see https://verilator.org/warn/UNSUPPORTED?v=latest
%Error: Internal Error: t/t_tri_and_eqcase.v:9:18: ../V3Ast.cpp:#: Null item passed to setOp2p
9 | logic b = 1'z === (clk1 & clk2);
| ^~~
... See the manual at https://verilator.org/verilator_doc.html for more assistance.

View File

@ -0,0 +1,19 @@
#!/usr/bin/env perl
if (!$::Driver) { use FindBin; exec("$FindBin::Bin/bootstrap.pl", @ARGV, $0); die; }
# DESCRIPTION: Verilator: Verilog Test driver/expect definition
#
# Copyright 2003 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(vlt => 1);
lint(
fails => $Self->{vlt_all},
expect_filename => $Self->{golden_filename},
);
ok(1);
1;

View File

@ -0,0 +1,17 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t (clk1, clk2);
input wire clk1, clk2;
logic b = 1'z === (clk1 & clk2);
always begin
if (!b) begin
$write("*-* All Finished *-*\n");
$finish;
end
end
endmodule

View File

@ -0,0 +1,21 @@
#!/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 Antmicro Ltd. 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(
);
execute(
check_finished => 1,
);
ok(1);
1;

View File

@ -0,0 +1,26 @@
// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain, for
// any use, without warranty, 2022 by Antmicro Ltd.
// SPDX-License-Identifier: CC0-1.0
module t (/*AUTOARG*/);
wire [3:0] a = 4'b11z1;
logic b = 1'bz === a[1];
logic c = 1'bz === a[2];
logic d = 2'bzz === 2'(a[1]);
logic e = 2'b0z === 2'(a[1]);
always begin
if (b && !c && !d && e) begin
$write("*-* All Finished *-*\n");
$finish;
end
else begin
$write("Error: b = %b, c = %b, d = %b, e = %b ", b, c, d, e);
$write("expected: b = 1, c = 0, d = 0, e = 1\n");
$stop;
end
end
endmodule