verilator/test_regress/t/t_constraint_implication_set.v

255 lines
6.9 KiB
Systemverilog

// DESCRIPTION: Verilator: Verilog Test module
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 PlanV GmbH
// SPDX-License-Identifier: CC0-1.0
// verilog_format: off
`define stop $stop
`define checkh(gotv,expv) do if ((gotv) !== (expv)) begin $write("%%Error: %s:%0d: got='h%x exp='h%x\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0)
`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)
// verilog_format: on
typedef enum bit [1:0] { TXN_READ, TXN_WRITE } txn_e;
// Each constraint exercises one RHS shape allowed by IEEE 1800-2023 18.7.2
// (constraint_set on the right of the implication operator -> ). `mode`
// selects which constraint contributes; the others are vacuous.
class Forms;
rand bit [3:0] mode;
rand bit [3:0] a;
rand bit [3:0] b;
rand bit [3:0] c;
rand bit [31:0] address;
rand txn_e txn_type;
rand bit [7:0] arr [4];
rand bit [3:0] uarr [3];
// Bare expression (legacy form, supported pre-PR via expr->expr).
constraint c_expr {
(mode == 4'd0) -> b == 4'h9;
}
// Brace-block: the exact shape from issue #7300.
constraint c_brace_repro {
(mode == 4'd1) -> {
if (txn_type inside {TXN_READ}) address % (1 << 4) == 0;
}
}
// Brace-block with multiple statements.
constraint c_brace_multi {
(mode == 4'd2) -> {
address[0] == 1'b0;
address[31] == 1'b0;
}
}
// Bare if (without surrounding braces).
constraint c_if {
(mode == 4'd3) -> if (a == 4'h1) b == 4'h7;
}
// Bare if/else.
constraint c_if_else {
(mode == 4'd4) -> if (a == 4'h1) b == 4'ha;
else b == 4'hb;
}
// Bare foreach.
constraint c_foreach {
(mode == 4'd5) -> foreach (arr[i]) arr[i] < 8'h40;
}
// Bare unique (uses the static-array form V3Randomize supports today).
constraint c_unique {
(mode == 4'd6) -> unique { uarr };
}
// Bare soft.
constraint c_soft {
(mode == 4'd7) -> soft b == 4'hd;
}
// Nested implication inside a brace block.
constraint c_nested {
(mode == 4'd8) -> {
(a == 4'h0) -> { b == 4'h5; }
}
}
endclass
// Conditional `disable soft` is meta-level: V3Randomize hoists it into a
// runtime AstIf attached to the setup task so the directive only fires
// when its outer condition is true. When override_flag==0, the soft
// `x == 4'h5` wins; when override_flag==1, the implication fires the
// disable AND the override `x == 4'hc`, so x must be 4'hc.
class DisSoft;
bit override_flag;
rand bit [3:0] x;
constraint c_soft_x { soft x == 4'h5; }
constraint c_override {
(override_flag == 1'b1) -> disable soft x ;
(override_flag == 1'b1) -> x == 4'hc ;
}
endclass
// MemberSel target for `disable soft`. Cross-object reference (inner.y)
// parses as AstMemberSel; disable-soft must extract the variable name from
// that node shape too.
class Inner;
rand bit [3:0] y;
endclass
class DisSoftMember;
bit override_flag;
rand Inner inner;
constraint c_soft_y { soft inner.y == 4'h5; }
constraint c_override {
(override_flag == 1'b1) -> disable soft inner.y ;
(override_flag == 1'b1) -> inner.y == 4'hc ;
}
function new(); inner = new(); endfunction
endclass
// `disable soft` in BOTH then and else arms of a single if. Exercises the
// hoist-list concat path in the else branch (a then-arm hoist already
// exists when the else-arm hoist is appended). We only check that the
// hard constraint in each arm fires correctly under its `pick` condition;
// soft enforcement priority is not asserted (Verilator's soft solver is
// best-effort). The condition variable `pick` is non-rand so the setup
// task sees its current value before the solver runs.
class DisSoftBothArms;
bit [1:0] pick;
rand bit [3:0] a;
rand bit [3:0] b;
constraint c_soft_a { soft a == 4'h7; }
constraint c_soft_b { soft b == 4'h8; }
constraint c_split {
if (pick == 2'd0) { disable soft a; a == 4'h1; }
else { disable soft b; b == 4'h2; }
}
endclass
module t;
Forms obj;
DisSoft ds;
int ok;
initial begin
obj = new();
repeat (10) begin
ok = obj.randomize() with { mode == 4'd0; };
`checkd(ok, 1);
`checkh(obj.b, 4'h9);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd1; txn_type == TXN_READ; };
`checkd(ok, 1);
`checkh(obj.address[3:0], 4'h0);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd2; };
`checkd(ok, 1);
`checkh(obj.address[0], 1'b0);
`checkh(obj.address[31], 1'b0);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd3; a == 4'h1; };
`checkd(ok, 1);
`checkh(obj.b, 4'h7);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd4; a == 4'h1; };
`checkd(ok, 1);
`checkh(obj.b, 4'ha);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd4; a == 4'h2; };
`checkd(ok, 1);
`checkh(obj.b, 4'hb);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd5; };
`checkd(ok, 1);
foreach (obj.arr[i]) `checkh(obj.arr[i] < 8'h40, 1'b1);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd6; };
`checkd(ok, 1);
`checkh(obj.uarr[0] == obj.uarr[1], 1'b0);
`checkh(obj.uarr[0] == obj.uarr[2], 1'b0);
`checkh(obj.uarr[1] == obj.uarr[2], 1'b0);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd7; };
`checkd(ok, 1);
`checkh(obj.b, 4'hd);
end
repeat (10) begin
ok = obj.randomize() with { mode == 4'd8; a == 4'h0; };
`checkd(ok, 1);
`checkh(obj.b, 4'h5);
end
ds = new();
ds.override_flag = 1'b0;
repeat (10) begin
ok = ds.randomize();
`checkd(ok, 1);
`checkh(ds.x, 4'h5);
end
ds.override_flag = 1'b1;
repeat (10) begin
ok = ds.randomize();
`checkd(ok, 1);
`checkh(ds.x, 4'hc);
end
begin
automatic DisSoftMember dsm = new();
dsm.override_flag = 1'b0;
repeat (10) begin
ok = dsm.randomize();
`checkd(ok, 1);
`checkh(dsm.inner.y, 4'h5);
end
dsm.override_flag = 1'b1;
repeat (10) begin
ok = dsm.randomize();
`checkd(ok, 1);
`checkh(dsm.inner.y, 4'hc);
end
end
begin
automatic DisSoftBothArms dba = new();
// pick==0: hard a==1 fires (then arm); hoisted disable_soft(a) runs
dba.pick = 2'd0;
repeat (10) begin
ok = dba.randomize();
`checkd(ok, 1);
`checkh(dba.a, 4'h1);
end
// pick==1: hard b==2 fires (else arm); hoisted disable_soft(b) runs
dba.pick = 2'd1;
repeat (10) begin
ok = dba.randomize();
`checkd(ok, 1);
`checkh(dba.b, 4'h2);
end
end
$write("*-* All Finished *-*\n");
$finish;
end
endmodule