From 4c92c035e7f51b776310f19992654d8c8a93ca2d Mon Sep 17 00:00:00 2001 From: Kornel Uriasz Date: Thu, 11 Jun 2026 15:43:18 +0200 Subject: [PATCH] Support reduction XOR/AND operations in constraints (#7753) --- docs/CONTRIBUTORS | 1 + src/V3Randomize.cpp | 35 ++++++++ test_regress/t/t_constraint_redops.py | 21 +++++ test_regress/t/t_constraint_redops.v | 123 ++++++++++++++++++++++++++ 4 files changed, 180 insertions(+) create mode 100755 test_regress/t/t_constraint_redops.py create mode 100644 test_regress/t/t_constraint_redops.v diff --git a/docs/CONTRIBUTORS b/docs/CONTRIBUTORS index b78b4f101..877011165 100644 --- a/docs/CONTRIBUTORS +++ b/docs/CONTRIBUTORS @@ -159,6 +159,7 @@ Kefa Chen Keith Colbert Kevin Kiningham Kevin Nygaard +Kornel Uriasz Kritik Bhimani Krzysztof Bieganski Krzysztof Boronski diff --git a/src/V3Randomize.cpp b/src/V3Randomize.cpp index 3fcaf5535..929bbf115 100644 --- a/src/V3Randomize.cpp +++ b/src/V3Randomize.cpp @@ -1509,6 +1509,41 @@ class ConstraintExprVisitor final : public VNVisitor { VL_DO_DANGLING(nodep->deleteTree(), nodep); iterate(sump); } + void visit(AstRedXor* nodep) override { + if (editFormat(nodep)) return; + + // Build popcount expansion: (extract x 1 1) ^ (extract x 2 2) ^ ... + FileLine* const fl = nodep->fileline(); + AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack(); + + AstNodeExpr* redxorp = new AstSel{fl, argp, 0, 1}; + redxorp->user1(true); + for (int i = 1; i < argp->width(); i++) { + AstSel* const selp = new AstSel{fl, argp->cloneTreePure(false), i, 1}; + selp->user1(true); + + redxorp = new AstXor{fl, redxorp, selp}; + redxorp->user1(true); + } + + nodep->replaceWith(redxorp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + iterate(redxorp); + } + void visit(AstRedAnd* nodep) override { + if (editFormat(nodep)) return; + // Convert to (~x == 0) + FileLine* const fl = nodep->fileline(); + AstNodeExpr* const argp = nodep->lhsp()->unlinkFrBack(); + const V3Number numZero{fl, argp->width(), 0}; + AstNodeExpr* const negp = new AstNot{fl, argp}; + negp->user1(true); + AstNodeExpr* const eqp = new AstEq{fl, negp, new AstConst{fl, numZero}}; + eqp->user1(true); + nodep->replaceWith(eqp); + VL_DO_DANGLING(nodep->deleteTree(), nodep); + iterate(eqp); + } void visit(AstRedOr* nodep) override { if (editFormat(nodep)) return; // Convert to (x != 0) diff --git a/test_regress/t/t_constraint_redops.py b/test_regress/t/t_constraint_redops.py new file mode 100755 index 000000000..db1adb3f9 --- /dev/null +++ b/test_regress/t/t_constraint_redops.py @@ -0,0 +1,21 @@ +#!/usr/bin/env python3 +# DESCRIPTION: Verilator: Verilog Test driver/expect definition +# +# 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-FileCopyrightText: 2026 Wilson Snyder +# SPDX-License-Identifier: LGPL-3.0-only OR Artistic-2.0 + +import vltest_bootstrap + +test.scenarios('simulator') + +if not test.have_solver: + test.skip("No constraint solver installed") + +test.compile() + +test.execute() + +test.passes() diff --git a/test_regress/t/t_constraint_redops.v b/test_regress/t/t_constraint_redops.v new file mode 100644 index 000000000..e285f8e24 --- /dev/null +++ b/test_regress/t/t_constraint_redops.v @@ -0,0 +1,123 @@ +// DESCRIPTION: Verilator: Verilog Test module +// +// This file ONLY is placed under the Creative Commons Public Domain. +// SPDX-FileCopyrightText: 2026 by Antmicro +// SPDX-License-Identifier: CC0-1.0 + +// Test case for reducing and in constraint +// verilog_format: off +`define stop $stop +`define checkd(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got=%0d exp=%0d\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0); +`define check_rand(cl, field, gotv, expv, count) \ +begin \ + automatic longint prev_result; \ + automatic int ok; \ + if (!bit'(cl.randomize())) $stop; \ + prev_result = longint'(field); \ + `checkd(gotv, expv) \ + repeat(count) begin \ + longint result; \ + if (!bit'(cl.randomize())) $stop; \ + result = longint'(field); \ + `checkd(gotv, expv) \ + if (result != prev_result) ok = 1; \ + prev_result = result; \ + end \ + if (ok != 1) $stop; \ +end +// verilog_format: on + +class test_redops_bitfields #(RANDVAL_BITWIDTH=8); + rand bit [RANDVAL_BITWIDTH-1:0] rand_val; + rand bit redand; + rand bit redxor; + rand bit redor; + + constraint c { + redand == &rand_val; + } + + constraint d { + redxor == ^rand_val; + } + + constraint e { + redor == |rand_val; + } + + function bit calc_redand(); + bit result = 1'b1; + + foreach (rand_val[idx]) begin + result &= rand_val[idx]; + end + + return result; + endfunction + + function bit calc_redxor(); + bit result; + + foreach (rand_val[idx]) begin + result ^= rand_val[idx]; + end + + return result; + endfunction + + function bit calc_redor(); + bit result = 1'b0; + + foreach (rand_val[idx]) begin + result |= rand_val[idx]; + end + + return result; + endfunction + + function void verify(); + //`check_rand(this, this.rand_val, this.redand, this.calc_redand(), 20); + `check_rand(this, this.rand_val, this.redxor, this.calc_redxor(), 20); + //`check_rand(this, this.rand_val, this.redor, this.calc_redor(), 20); + endfunction +endclass + +module t; + test_redops_bitfields #(.RANDVAL_BITWIDTH(1)) redops_1bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(8)) redops_8bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(16)) redops_16bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(32)) redops_32bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(47)) redops_47bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(63)) redops_63bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(64)) redops_64bit; + test_redops_bitfields #(.RANDVAL_BITWIDTH(128)) redops_128bit; + + initial begin + redops_1bit = new(); + redops_1bit.verify(); + + redops_8bit = new(); + redops_8bit.verify(); + + redops_16bit = new(); + redops_16bit.verify(); + + redops_32bit = new(); + redops_32bit.verify(); + + redops_47bit = new(); + redops_47bit.verify(); + + redops_63bit = new(); + redops_63bit.verify(); + + redops_64bit = new(); + redops_64bit.verify(); + + redops_128bit = new(); + redops_128bit.verify(); + + $write("*-* All Finished *-*\n"); + $finish; + end +endmodule