From 9632c614beabc7af3dfd3aa81573248607a587fb Mon Sep 17 00:00:00 2001 From: Michael Bikovitsky Date: Sun, 23 Nov 2025 03:09:49 +0200 Subject: [PATCH] Fix X handling in UDPs (#6723) --- src/V3Udp.cpp | 17 ++++++- test_regress/t/t_udp_nonsequential_x.py | 18 +++++++ test_regress/t/t_udp_nonsequential_x.v | 49 ++++++++++++++++++ test_regress/t/t_udp_sequential_x.py | 18 +++++++ test_regress/t/t_udp_sequential_x.v | 66 +++++++++++++++++++++++++ 5 files changed, 167 insertions(+), 1 deletion(-) create mode 100755 test_regress/t/t_udp_nonsequential_x.py create mode 100644 test_regress/t/t_udp_nonsequential_x.v create mode 100755 test_regress/t/t_udp_sequential_x.py create mode 100644 test_regress/t/t_udp_sequential_x.v diff --git a/src/V3Udp.cpp b/src/V3Udp.cpp index f811077ab..e222285b9 100644 --- a/src/V3Udp.cpp +++ b/src/V3Udp.cpp @@ -138,6 +138,12 @@ class UdpVisitor final : public VNVisitor { fl, logandp, new AstLogNot{fl, new AstVarRef{fl, varp, VAccess::READ}}}; } else if (valName == "1" || valName == "r") { logandp = new AstLogAnd{fl, logandp, new AstVarRef{fl, varp, VAccess::READ}}; + } else if (valName == "x" || valName == "X") { + // No x inputs supported yet, so this whole table line + // can never match. Drop the whole thing. + if (edgetrigp) pushDeletep(edgetrigp); + if (logandp) pushDeletep(logandp); + return; } } iNodep = iNodep->nextp(); @@ -166,6 +172,12 @@ class UdpVisitor final : public VNVisitor { } else if (oNodep->name() == "1") { logandp = new AstLogAnd{fl, logandp, new AstVarRef{fl, m_oFieldVarp, VAccess::READ}}; + } else if (oNodep->name() == "x" || oNodep->name() == "X") { + // No x inputs supported yet, so this whole table line + // can never match. Drop the whole thing. + if (edgetrigp) pushDeletep(edgetrigp); + if (logandp) pushDeletep(logandp); + return; } } @@ -192,6 +204,7 @@ class UdpVisitor final : public VNVisitor { void visit(AstLogNot* nodep) override { iterateChildren(nodep); } // For logic processing. bool isEdgeTrig(std::string& valName) { + if (valName == "x" || valName == "X") return false; if (valName == "*") return true; if (valName == "01" || valName == "p" || valName == "P" || valName == "r" || valName == "R") { @@ -204,7 +217,9 @@ class UdpVisitor final : public VNVisitor { return true; } if (valName.size() == 2) { - if (valName[0] == '1' || valName[1] == '0') + if (valName[0] == 'x' || valName[0] == 'X' || valName[1] == 'x' || valName[1] == 'X') + valName = "x"; + else if (valName[0] == '1' || valName[1] == '0') valName = "f"; else if (valName[0] == '0' || valName[1] == '1') valName = "r"; diff --git a/test_regress/t/t_udp_nonsequential_x.py b/test_regress/t/t_udp_nonsequential_x.py new file mode 100755 index 000000000..9e4a474a0 --- /dev/null +++ b/test_regress/t/t_udp_nonsequential_x.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('vlt_all') + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_udp_nonsequential_x.v b/test_regress/t/t_udp_nonsequential_x.v new file mode 100644 index 000000000..d2643cd5e --- /dev/null +++ b/test_regress/t/t_udp_nonsequential_x.v @@ -0,0 +1,49 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Michael Bikovitsky. +// SPDX-License-Identifier: CC0-1.0 + +module t (); + + wire true1; + not1 a(true1, '0); + + wire false1; + not1 b(false1, '1); + + wire true2; + not1 c(true2, '0); + + wire false2; + not1 d(false2, '1); + + initial begin + if (true1 != '1) $stop; + if (false1 != '0) $stop; + if (true2 != '1) $stop; + if (false2 != '0) $stop; + $finish; + end + +endmodule + +primitive not1 (q, d); + output q; + input d; + table + 0 : 1; + 1 : 0; + x : x; + endtable +endprimitive + +primitive not2 (q, d); + output q; + input d; + table + 0 : 1; + 1 : 0; + X : X; + endtable +endprimitive diff --git a/test_regress/t/t_udp_sequential_x.py b/test_regress/t/t_udp_sequential_x.py new file mode 100755 index 000000000..3ed3afcba --- /dev/null +++ b/test_regress/t/t_udp_sequential_x.py @@ -0,0 +1,18 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# Copyright 2025 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 + +import vltest_bootstrap + +test.scenarios('vlt_all') + +test.compile(verilator_flags2=["--x-assign", "1"]) + +test.execute() + +test.passes() diff --git a/test_regress/t/t_udp_sequential_x.v b/test_regress/t/t_udp_sequential_x.v new file mode 100644 index 000000000..222ea9b1f --- /dev/null +++ b/test_regress/t/t_udp_sequential_x.v @@ -0,0 +1,66 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain, for +// any use, without warranty, 2025 by Michael Bikovitsky. +// SPDX-License-Identifier: CC0-1.0 + +module t ( + input wire clk + ); + + wire q1; + pos i_pos(q1, clk); + + wire q2; + neg i_neg(q2, clk); + + integer cycle = 0; + always @(posedge clk) begin + if (cycle == 10) $finish; + cycle <= cycle + 1; + end + + always @(q1 or q2) begin + // q1 should be assigned to 0 on every posedge and to X otherwise. + // So when the value of q1 changes, *and* clk is positive, we expect + // q1 to be 1. + // Same for q2, but on the negedge. + if (clk && q1) $stop; + if (!clk && q2) $stop; + end + +endmodule + +primitive pos (q, clk); + output q; + reg q; + input clk; + table + (01) : ? : 0; + // Explicitly set the output to X on clk 0->X edge. + // This edge can never happen in Verilator. + // If all edges *from* 0 are treated as rising edges, this will cause + // the output to be 1, since we use --x-assign 1, and the test will fail. + // (Actually this depends on the evaluation order of the always blocks + // that V3Udp.cpp creates, but at the time of writing they seem to be + // evaluated in the order of the lines in the table.) + (0x) : ? : x; + endtable +endprimitive + +primitive neg (q, clk); + output q; + reg q; + input clk; + table + (10) : ? : 0; + // Explicitly set the output to X on clk X->0 edge. + // This edge can never happen in Verilator. + // If all edges *to* 0 are treated as falling edges, this will cause + // the output to be 1, since we use --x-assign 1, and the test will fail. + // (Actually this depends on the evaluation order of the always blocks + // that V3Udp.cpp creates, but at the time of writing they seem to be + // evaluated in the order of the lines in the table.) + (x0) : ? : x; + endtable +endprimitive