255 lines
6.9 KiB
Systemverilog
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
|