From 429474013d7414fc2297a57e492244030e2fbf66 Mon Sep 17 00:00:00 2001 From: "D. Jeff Dionne" Date: Sun, 21 Jun 2026 18:45:52 +0900 Subject: [PATCH 1/3] testsuite: add write_vhdl/issue227 -- failing tests for conditional-in-aggregate write_vhdl emits illegal VHDL-93 when boolean/comparison cells have output width > 1: n0 <= (0 => '1' when COND else '0', others => '0'); VHDL-93 s7.3.2 forbids conditional expressions inside aggregates. GHDL rejects this with "')' is expected instead of 'when'". Five test modules, all failing before the fix: t_exact: verbatim reporter case ($logic_and y_width=3) t_logic: $logic_and/or/not at y_width=4 (broken) and y_width=1 (control) t_reduce: $reduce_and/or/xor/xnor at y_width=4 and y_width=1; $reduce_xnor has zero prior test coverage at any width t_compare: $eq/$ne/$lt/$eqx at y_width=4 and y_width=1; $eqx has zero prior test coverage at any width t_combined: three independent boolean cells ($logic_and/or/not) feeding one 4-bit output mixed with a plain signal and a constant; asserts exactly 3 std_logic intermediates post-fix -- one per cell, proving the fix is per-cell not a merged expression Each module includes y_width=1 control outputs exercising the same cell types on the already-correct path, confirming the fix does not regress it. Ref: https://github.com/ghdl/ghdl-yosys-plugin/issues/227 Co-authored-by: Claude Sonnet 4.6 --- testsuite/write_vhdl/issue227/t_combined.v | 36 +++++ testsuite/write_vhdl/issue227/t_compare.v | 35 +++++ testsuite/write_vhdl/issue227/t_exact.v | 14 ++ testsuite/write_vhdl/issue227/t_logic.v | 29 ++++ testsuite/write_vhdl/issue227/t_reduce.v | 35 +++++ testsuite/write_vhdl/issue227/testsuite.sh | 148 +++++++++++++++++++++ 6 files changed, 297 insertions(+) create mode 100644 testsuite/write_vhdl/issue227/t_combined.v create mode 100644 testsuite/write_vhdl/issue227/t_compare.v create mode 100644 testsuite/write_vhdl/issue227/t_exact.v create mode 100644 testsuite/write_vhdl/issue227/t_logic.v create mode 100644 testsuite/write_vhdl/issue227/t_reduce.v create mode 100755 testsuite/write_vhdl/issue227/testsuite.sh diff --git a/testsuite/write_vhdl/issue227/t_combined.v b/testsuite/write_vhdl/issue227/t_combined.v new file mode 100644 index 0000000..6fce009 --- /dev/null +++ b/testsuite/write_vhdl/issue227/t_combined.v @@ -0,0 +1,36 @@ +// t_combined.v -- multiple independent boolean cells feeding one output +// +// This is the structural test for the key property of the fix: +// N independent boolean cells => exactly N std_logic intermediates. +// +// Three independent boolean cells (distinct inputs, no CSE possible), +// OR'd together with a plain signal and a constant into a single 4-bit +// output. Each boolean cell has Y_WIDTH=4 and must get its own +// std_logic intermediate. The plain signal and constant require none. +// +// A broken "combined expression" fix would produce one intermediate +// (or none) where three are required. The test asserts the exact count. +// +// Expected RTLIL: 3 x $logic_and/or/not at Y_WIDTH=4, 4 x $or at Y_WIDTH=4. +// Expected VHDL after fix: exactly 3 'signal nXX : std_logic;' declarations. +module t_combined ( + input wire [3:0] vec_a, + input wire bool_c, bool_d, bool_e, bool_f, + output wire [3:0] o +); + // (bool_c && bool_d): $logic_and Y_WIDTH=4 -- intermediate required + // (bool_d || bool_e): $logic_or Y_WIDTH=4 -- intermediate required + // !bool_f: $logic_not Y_WIDTH=4 -- intermediate required + // vec_a: plain signal -- no intermediate + // 4'b1010: constant -- no intermediate + // + // Each boolean uses distinct inputs so Yosys cannot merge any two: + // and: bool_c, bool_d + // or: bool_d, bool_e (bool_d shared with and -- but different cell type) + // not: bool_f (unique input) + assign o = (bool_c && bool_d) + | (bool_d || bool_e) + | !bool_f + | vec_a + | 4'b1010; +endmodule diff --git a/testsuite/write_vhdl/issue227/t_compare.v b/testsuite/write_vhdl/issue227/t_compare.v new file mode 100644 index 0000000..c204d08 --- /dev/null +++ b/testsuite/write_vhdl/issue227/t_compare.v @@ -0,0 +1,35 @@ +// t_compare.v -- comparison cells ($eq, $ne, $lt, $eqx) with y_width > 1 +// +// Each cell type is exercised at two output widths: +// y_width=4 (broken path): must produce one std_logic intermediate each +// y_width=1 (control): must stay as direct '1' when COND else '0' +// +// Operand pairs are rotated between y_width=4 and y_width=1 outputs so +// Yosys cannot merge them via CSE. +// +// $eqx (casex equality, generated by ===) has zero prior test coverage. +// $ne, $lt cover the ne/lt/le/ge/gt code path (all share the same template). +// +// Expected RTLIL: 4 x comparison cells at Y_WIDTH=4, 4 at Y_WIDTH=1. +// Expected VHDL after fix: 4 new 'signal nXX : std_logic;' declarations. +module t_compare ( + input wire [3:0] vec_a, vec_b, vec_c, + output wire [3:0] o_eq4, // $eq y_width=4 (broken before fix) + output wire [3:0] o_ne4, // $ne y_width=4 (broken before fix) + output wire [3:0] o_lt4, // $lt y_width=4 (broken before fix) + output wire [3:0] o_eqx4, // $eqx y_width=4 (broken before fix) + output wire o_eq1, // $eq y_width=1 (control: must be unchanged) + output wire o_ne1, // $ne y_width=1 (control: must be unchanged) + output wire o_lt1, // $lt y_width=1 (control: must be unchanged) + output wire o_eqx1 // $eqx y_width=1 (control: must be unchanged) +); + assign o_eq4 = (vec_a == vec_b); // $eq Y_WIDTH=4 + assign o_ne4 = (vec_b != vec_c); // $ne Y_WIDTH=4 + assign o_lt4 = (vec_a < vec_c); // $lt Y_WIDTH=4 + assign o_eqx4 = (vec_a === vec_b); // $eqx Y_WIDTH=4 + + assign o_eq1 = (vec_a == vec_c); // $eq Y_WIDTH=1 -- different pair + assign o_ne1 = (vec_a != vec_b); // $ne Y_WIDTH=1 -- different pair + assign o_lt1 = (vec_b < vec_c); // $lt Y_WIDTH=1 -- different pair + assign o_eqx1 = (vec_b === vec_c); // $eqx Y_WIDTH=1 -- different pair +endmodule diff --git a/testsuite/write_vhdl/issue227/t_exact.v b/testsuite/write_vhdl/issue227/t_exact.v new file mode 100644 index 0000000..c841538 --- /dev/null +++ b/testsuite/write_vhdl/issue227/t_exact.v @@ -0,0 +1,14 @@ +// t_exact.v -- exact reproduction of ghdl-yosys-plugin issue #227 +// +// write_vhdl must produce valid VHDL-93 for o0 (y_width=3). +// o1 is the same $logic_and but y_width=1; it must be unchanged by any fix. +module t_exact ( + input wire [2:0] i0, + input wire i1, + input wire i2, + output wire [2:0] o0, // $logic_and result zero-extended to 3 bits (broken) + output wire o1 // $logic_and result 1 bit (control: must stay correct) +); + assign o0 = i0 | (i1 && i2); // y_width=3: was broken + assign o1 = i1 && i2; // y_width=1: must remain a direct conditional +endmodule diff --git a/testsuite/write_vhdl/issue227/t_logic.v b/testsuite/write_vhdl/issue227/t_logic.v new file mode 100644 index 0000000..42bc9e2 --- /dev/null +++ b/testsuite/write_vhdl/issue227/t_logic.v @@ -0,0 +1,29 @@ +// t_logic.v -- $logic_and, $logic_or, $logic_not with y_width > 1 +// +// Each cell type is exercised at two output widths: +// y_width=4 (broken path): must produce one std_logic intermediate each +// y_width=1 (control): must stay as direct '1' when COND else '0' +// +// Inputs are rotated between the y_width=4 and y_width=1 outputs so Yosys +// cannot merge them via CSE. +// +// Expected RTLIL: 3 x $logic_and/or/not at Y_WIDTH=4, 3 at Y_WIDTH=1. +// Expected VHDL after fix: 3 new 'signal nXX : std_logic;' declarations, +// 3 existing 'signal nXX : std_logic;' for the y_width=1 internal wires. +module t_logic ( + input wire bool_c, bool_d, bool_e, bool_f, + output wire [3:0] o_and4, // $logic_and y_width=4 (broken before fix) + output wire [3:0] o_or4, // $logic_or y_width=4 (broken before fix) + output wire [3:0] o_not4, // $logic_not y_width=4 (broken before fix) + output wire o_and1, // $logic_and y_width=1 (control: must be unchanged) + output wire o_or1, // $logic_or y_width=1 (control: must be unchanged) + output wire o_not1 // $logic_not y_width=1 (control: must be unchanged) +); + assign o_and4 = bool_c && bool_d; // $logic_and Y_WIDTH=4 + assign o_or4 = bool_c || bool_e; // $logic_or Y_WIDTH=4 + assign o_not4 = !bool_d; // $logic_not Y_WIDTH=4 + + assign o_and1 = bool_e && bool_f; // $logic_and Y_WIDTH=1 -- different inputs + assign o_or1 = bool_d || bool_f; // $logic_or Y_WIDTH=1 -- different inputs + assign o_not1 = !bool_f; // $logic_not Y_WIDTH=1 -- different input +endmodule diff --git a/testsuite/write_vhdl/issue227/t_reduce.v b/testsuite/write_vhdl/issue227/t_reduce.v new file mode 100644 index 0000000..7f0c6ef --- /dev/null +++ b/testsuite/write_vhdl/issue227/t_reduce.v @@ -0,0 +1,35 @@ +// t_reduce.v -- $reduce_and, $reduce_or, $reduce_xor, $reduce_xnor with y_width > 1 +// +// Each cell type is exercised at two output widths: +// y_width=4 (broken path): must produce one std_logic intermediate each +// y_width=1 (control): must stay as direct '1' when COND else '0' +// +// vec_a drives the y_width=4 outputs; vec_b drives the y_width=1 controls +// so Yosys cannot merge them via CSE. +// +// $reduce_xnor has zero prior test coverage at any width. +// +// Expected RTLIL: 4 x reduce_* at Y_WIDTH=4, 4 at Y_WIDTH=1. +// Expected VHDL after fix: 4 new 'signal nXX : std_logic;' declarations. +module t_reduce ( + input wire [3:0] vec_a, + input wire [3:0] vec_b, + output wire [3:0] o_and4, // $reduce_and y_width=4 (broken before fix) + output wire [3:0] o_or4, // $reduce_or y_width=4 (broken before fix) + output wire [3:0] o_xor4, // $reduce_xor y_width=4 (broken before fix) + output wire [3:0] o_xnor4, // $reduce_xnor y_width=4 (broken before fix) + output wire o_and1, // $reduce_and y_width=1 (control: must be unchanged) + output wire o_or1, // $reduce_or y_width=1 (control: must be unchanged) + output wire o_xor1, // $reduce_xor y_width=1 (control: must be unchanged) + output wire o_xnor1 // $reduce_xnor y_width=1 (control: must be unchanged) +); + assign o_and4 = &vec_a; // $reduce_and Y_WIDTH=4 + assign o_or4 = |vec_a; // $reduce_or Y_WIDTH=4 + assign o_xor4 = ^vec_a; // $reduce_xor Y_WIDTH=4 + assign o_xnor4 = ~^vec_a; // $reduce_xnor Y_WIDTH=4 + + assign o_and1 = &vec_b; // $reduce_and Y_WIDTH=1 -- different input + assign o_or1 = |vec_b; // $reduce_or Y_WIDTH=1 -- different input + assign o_xor1 = ^vec_b; // $reduce_xor Y_WIDTH=1 -- different input + assign o_xnor1 = ~^vec_b; // $reduce_xnor Y_WIDTH=1 -- different input +endmodule diff --git a/testsuite/write_vhdl/issue227/testsuite.sh b/testsuite/write_vhdl/issue227/testsuite.sh new file mode 100755 index 0000000..ac420f0 --- /dev/null +++ b/testsuite/write_vhdl/issue227/testsuite.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# Test: write_vhdl issue #227 -- conditional expression in aggregate +# +# write_vhdl emitted illegal VHDL-93 for boolean/comparison cells with +# output width > 1: +# +# n0 <= (0 => '1' when COND else '0', others => '0'); +# +# VHDL-93 s7.3.2 forbids conditional expressions inside aggregates. +# GHDL rejects this with "')' is expected instead of 'when'". +# +# Five test modules: +# t_exact: exact reporter case ($logic_and y_width=3) +# t_logic: $logic_and/or/not at y_width=4 (broken) and y_width=1 (control) +# t_reduce: $reduce_and/or/xor/xnor at y_width=4 and y_width=1 +# t_compare: $eq/$ne/$lt/$eqx at y_width=4 and y_width=1 +# t_combined: three independent boolean cells feeding one output; +# asserts exactly 3 intermediates (one per cell, not combined) + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +. "$SCRIPT_DIR/../../rename/common.sh" +check_tools + +# --------------------------------------------------------------------------- +# run_test