From 46b8dca3602111f464053a4ce97c84fbc830858a Mon Sep 17 00:00:00 2001 From: Ryszard Rozak Date: Sat, 1 Oct 2022 16:34:30 +0200 Subject: [PATCH] Add handling of tristate select/extend (#3604) --- src/V3Tristate.cpp | 75 +++++++++++++++++++++------ test_regress/t/t_tri_and_eqcase.out | 8 +++ test_regress/t/t_tri_and_eqcase.pl | 19 +++++++ test_regress/t/t_tri_and_eqcase.v | 17 ++++++ test_regress/t/t_tri_select_eqcase.pl | 21 ++++++++ test_regress/t/t_tri_select_eqcase.v | 26 ++++++++++ 6 files changed, 149 insertions(+), 17 deletions(-) create mode 100644 test_regress/t/t_tri_and_eqcase.out create mode 100755 test_regress/t/t_tri_and_eqcase.pl create mode 100644 test_regress/t/t_tri_and_eqcase.v create mode 100755 test_regress/t/t_tri_select_eqcase.pl create mode 100644 test_regress/t/t_tri_select_eqcase.v diff --git a/src/V3Tristate.cpp b/src/V3Tristate.cpp index 55dddaacc..7e35067eb 100644 --- a/src/V3Tristate.cpp +++ b/src/V3Tristate.cpp @@ -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: "); diff --git a/test_regress/t/t_tri_and_eqcase.out b/test_regress/t/t_tri_and_eqcase.out new file mode 100644 index 000000000..315a41e1f --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.out @@ -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. diff --git a/test_regress/t/t_tri_and_eqcase.pl b/test_regress/t/t_tri_and_eqcase.pl new file mode 100755 index 000000000..48bf31461 --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.pl @@ -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; diff --git a/test_regress/t/t_tri_and_eqcase.v b/test_regress/t/t_tri_and_eqcase.v new file mode 100644 index 000000000..204aab82d --- /dev/null +++ b/test_regress/t/t_tri_and_eqcase.v @@ -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 diff --git a/test_regress/t/t_tri_select_eqcase.pl b/test_regress/t/t_tri_select_eqcase.pl new file mode 100755 index 000000000..f5e338520 --- /dev/null +++ b/test_regress/t/t_tri_select_eqcase.pl @@ -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; diff --git a/test_regress/t/t_tri_select_eqcase.v b/test_regress/t/t_tri_select_eqcase.v new file mode 100644 index 000000000..c70688c0f --- /dev/null +++ b/test_regress/t/t_tri_select_eqcase.v @@ -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