mirror of
https://github.com/verilator/verilator.git
synced 2026-10-06 01:54:01 +02:00
Support iff in covergroup bins (#8406)
This commit is contained in:
@@ -1067,7 +1067,7 @@ class AstCoverBin final : public AstNode {
|
||||
|
||||
public:
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNode* rangesp, bool isIgnore, bool isIllegal,
|
||||
bool isWildcard = false)
|
||||
bool isWildcard = false, AstNodeExpr* iffp = nullptr)
|
||||
: ASTGEN_SUPER_CoverBin(fl)
|
||||
, m_name{name}
|
||||
, m_binsType{isIllegal ? VCoverBinsType::BINS_ILLEGAL
|
||||
@@ -1076,6 +1076,7 @@ public:
|
||||
: VCoverBinsType::BINS_USER))}
|
||||
, m_isWildcard{isWildcard} {
|
||||
addRangesp(rangesp);
|
||||
this->iffp(iffp);
|
||||
}
|
||||
// Constructor for automatic bins
|
||||
AstCoverBin(FileLine* fl, const string& name, AstNodeExpr* arraySizep)
|
||||
|
||||
@@ -1017,6 +1017,7 @@ class FunctionalCoverageVisitor final : public VNVisitor {
|
||||
+ " hit in coverpoint "
|
||||
+ coverpointp->prettyNameQ()));
|
||||
}
|
||||
if (binp->iffp()) condp = new AstLogAnd{fl, binp->iffp()->cloneTree(false), condp};
|
||||
AstNodeExpr* const guardedp = applyCoverpointIffCondition(coverpointp, fl, condp);
|
||||
UASSERT_OBJ(m_sampleFuncp, binp, "sample() CFunc not set for coverpoint");
|
||||
m_sampleFuncp->addStmtsp(new AstIf{fl, guardedp, actionp, nullptr});
|
||||
|
||||
@@ -352,6 +352,11 @@ class EmitVBaseVisitorConst VL_NOT_FINAL : public VNVisitorConst {
|
||||
}
|
||||
puts("}");
|
||||
}
|
||||
if (nodep->iffp()) {
|
||||
puts(" iff (");
|
||||
iterateConst(nodep->iffp());
|
||||
puts(")");
|
||||
}
|
||||
puts(";\n");
|
||||
}
|
||||
void visit(AstCoverBinsof* nodep) override {
|
||||
|
||||
+1
-1
@@ -2101,7 +2101,7 @@ class WidthVisitor final : public VNVisitor {
|
||||
}
|
||||
void visit(AstCoverBin* nodep) override {
|
||||
widthCovergroupRanges(nodep->rangesp());
|
||||
userIterateAndNext(nodep->iffp(), nullptr);
|
||||
if (nodep->iffp()) iterateCheckBool(nodep, "iff condition", nodep->iffp(), BOTH);
|
||||
userIterateAndNext(nodep->arraySizep(), nullptr);
|
||||
userIterateAndNext(nodep->transp(), nullptr);
|
||||
}
|
||||
|
||||
+9
-12
@@ -7169,9 +7169,9 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
|
||||
coverage_option { $$ = $1; }
|
||||
// // Can't use wildcardE as results in conflicts
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false, false, $8};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
$$ = binp; }
|
||||
| yBINS idAny/*bin_identifier*/ '[' cgexpr ']' iffE
|
||||
{ // Check for automatic bins: bins auto[N]
|
||||
if (*$2 == "auto") {
|
||||
@@ -7184,13 +7184,13 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
|
||||
}
|
||||
}
|
||||
| yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false};
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, true, false, false, $8};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
$$ = binp; }
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true};
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, true, false, $8};
|
||||
if ($3) binp->isArray(true);
|
||||
$$ = binp; DEL($8); }
|
||||
$$ = binp; }
|
||||
| yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ AstCoverBin* const binp = new AstCoverBin{$<fl>2, *$2, $6, false, false};
|
||||
BBCOVERIGN($<fl>8, "Unsupported: 'with' in cover bin (bin created without filter)");
|
||||
@@ -7210,14 +7210,11 @@ bins_or_options<nodep>: // ==IEEE: bins_or_options
|
||||
| yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' id/*cover_point_id*/ yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>6, "Unsupported: 'with' in cover bin"); DEL($8, $10); }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true};
|
||||
DEL($9); }
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, false, true, $9}; }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true};
|
||||
DEL($9); }
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, true, false, true, $9}; }
|
||||
| yWILDCARD yILLEGAL_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' iffE
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true};
|
||||
DEL($9); }
|
||||
{ $$ = new AstCoverBin{$<fl>3, *$3, $7, false, true, true, $9}; }
|
||||
| yWILDCARD yBINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
{ $$ = nullptr; BBCOVERIGN($<fl>9, "Unsupported: 'with' in wildcard cover bin"); DEL($7, $11, $13); }
|
||||
| yWILDCARD yIGNORE_BINS idAny/*bin_identifier*/ bins_orBraE '=' '{' range_list '}' yWITH__PAREN '(' cgexpr ')' iffE
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
__vlAnonCG_cg.cp_a.one: 2
|
||||
__vlAnonCG_cg.cp_a.one: 1
|
||||
__vlAnonCG_cg.cp_b.one: 2
|
||||
__vlAnonCG_cg.cross_ab.one_x_one [cross]: 1
|
||||
cg_and.cp_concat.b00: 0
|
||||
@@ -8,6 +8,9 @@ cg_and.cp_concat.b11: 0
|
||||
cg_array_iff.cp.arr[0]: 1
|
||||
cg_array_iff.cp.arr[1]: 0
|
||||
cg_array_iff.cp.arr[2]: 0
|
||||
cg_bin_cross.cp_a.one: 2
|
||||
cg_bin_cross.cp_b.one: 2
|
||||
cg_bin_cross.cross_ab.one_x_one [cross]: 1
|
||||
cg_bitw.cp.seven: 1
|
||||
cg_cross_coverpoint_iff.cp_a.one: 2
|
||||
cg_cross_coverpoint_iff.cp_b.one: 2
|
||||
@@ -29,5 +32,14 @@ cg_or.cp.lo: 1
|
||||
cg_part.cp.one: 1
|
||||
cg_rel.cp.five: 1
|
||||
cg_rel.cp.two: 0
|
||||
cg_state_bin_iff.cp.grouped: 2
|
||||
cg_state_bin_iff.cp.ignored [ignore]: 1
|
||||
cg_state_bin_iff.cp.illegal [illegal]: 0
|
||||
cg_state_bin_iff.cp.scalar: 1
|
||||
cg_state_bin_iff.cp.values[0]: 1
|
||||
cg_state_bin_iff.cp.values[1]: 1
|
||||
cg_state_bin_iff.cp.wild: 2
|
||||
cg_state_bin_iff.cp.wild_ignored [ignore]: 2
|
||||
cg_state_bin_iff.cp.wild_illegal [illegal]: 0
|
||||
cg_trans2_iff.cp.t2: 1
|
||||
cg_trans3_iff.cp.t3: 1
|
||||
|
||||
@@ -27,9 +27,11 @@ class CrossIffEmbedded;
|
||||
bit b;
|
||||
bit enabled;
|
||||
|
||||
// cross-level iff using an enclosing class member
|
||||
// Bin- and cross-level iff using an enclosing class member.
|
||||
covergroup cg;
|
||||
cp_a: coverpoint a {bins one = {1};}
|
||||
cp_a: coverpoint a {
|
||||
bins one = {1} iff (this.enabled);
|
||||
}
|
||||
cp_b: coverpoint b {bins one = {1};}
|
||||
cross_ab: cross cp_a, cp_b iff (this.enabled);
|
||||
endgroup
|
||||
@@ -56,6 +58,10 @@ module t;
|
||||
logic [1:0] m_a;
|
||||
logic [1:0] m_b;
|
||||
int count;
|
||||
logic [3:0] bin_value;
|
||||
bit point_enable;
|
||||
bit bin_enable;
|
||||
logic [2:0] bin_mask;
|
||||
|
||||
// iff on explicit value bins
|
||||
covergroup cg_iff;
|
||||
@@ -144,6 +150,29 @@ module t;
|
||||
cross cp_a, cp_b iff (!disable_cross) {}
|
||||
endgroup
|
||||
|
||||
covergroup cg_state_bin_iff;
|
||||
cp: coverpoint bin_value iff (point_enable) {
|
||||
bins scalar = {2} iff (bin_enable);
|
||||
bins values[] = {0, 1} iff (bin_enable && bin_mask[0]);
|
||||
bins grouped = {3, 4} iff (bin_enable && (bin_mask[2:1] != 0));
|
||||
wildcard bins wild = {4'b01?1} iff (bin_enable && !(bin_mask[2] == 0));
|
||||
ignore_bins ignored = {8} iff (bin_enable);
|
||||
wildcard ignore_bins wild_ignored = {4'b10?1} iff (bin_enable && bin_mask[1]);
|
||||
illegal_bins illegal = {12} iff (bin_enable);
|
||||
wildcard illegal_bins wild_illegal = {4'b11?1} iff (bin_enable && bin_mask[2]);
|
||||
}
|
||||
endgroup
|
||||
|
||||
covergroup cg_bin_cross with function sample (bit a, bit b, bit enable_a, bit enable_b);
|
||||
cp_a: coverpoint a {
|
||||
bins one = {1} iff (enable_a);
|
||||
}
|
||||
cp_b: coverpoint b {
|
||||
bins one = {1} iff (enable_b);
|
||||
}
|
||||
cross_ab: cross cp_a, cp_b;
|
||||
endgroup
|
||||
|
||||
cg_iff cg1 = new;
|
||||
cg_default_iff cg2 = new;
|
||||
cg_array_iff cg3 = new;
|
||||
@@ -158,6 +187,8 @@ module t;
|
||||
cg_cross_iff cx = new;
|
||||
cg_cross_iff_unnamed cxu = new;
|
||||
CrossIffEmbedded cxe = new;
|
||||
cg_state_bin_iff state_bins = new;
|
||||
cg_bin_cross cxb = new;
|
||||
|
||||
initial begin
|
||||
cxc.sample(1, 1, 0, 1);
|
||||
@@ -172,6 +203,42 @@ module t;
|
||||
cxe.observe(1, 1, 0);
|
||||
cxe.observe(1, 1, 1);
|
||||
|
||||
cxb.sample(1, 1, 0, 0);
|
||||
`checkr(cxb.get_inst_coverage(), 0.0);
|
||||
cxb.sample(1, 1, 1, 0);
|
||||
cxb.sample(1, 1, 0, 1);
|
||||
`checkr(cxb.get_inst_coverage() < 100.0, 1);
|
||||
cxb.sample(1, 1, 1, 1);
|
||||
`checkr(cxb.get_inst_coverage(), 100.0);
|
||||
cxb.sample(1, 1, 0, 0);
|
||||
|
||||
// IEEE 1800-2023 19.5.1: iff gates sampling, not bin construction.
|
||||
point_enable = 1;
|
||||
bin_enable = 0;
|
||||
bin_mask = 3'b111;
|
||||
for (int i = 0; i < 8; ++i) begin
|
||||
bin_value = 4'(i);
|
||||
state_bins.sample();
|
||||
end
|
||||
`checkr(state_bins.get_inst_coverage(), 0.0);
|
||||
for (int i = 8; i < 16; ++i) begin
|
||||
bin_value = 4'(i);
|
||||
state_bins.sample();
|
||||
end
|
||||
point_enable = 0;
|
||||
bin_enable = 1;
|
||||
for (int i = 0; i < 16; ++i) begin
|
||||
bin_value = 4'(i);
|
||||
state_bins.sample();
|
||||
end
|
||||
`checkr(state_bins.get_inst_coverage(), 0.0);
|
||||
point_enable = 1;
|
||||
for (int i = 0; i < 12; ++i) begin
|
||||
bin_value = 4'(i);
|
||||
state_bins.sample();
|
||||
end
|
||||
`checkr(state_bins.get_inst_coverage(), 100.0);
|
||||
|
||||
// Sample disabled_lo and disabled_hi with enable=0 -- must not be recorded
|
||||
enable = 0;
|
||||
value = 1;
|
||||
|
||||
@@ -0,0 +1,4 @@
|
||||
Disabled guards produced no illegal-bin errors.
|
||||
[0] %Error: t_covergroup_iff_illegal.v:18: Assertion failed in top.t.cg.sample: Illegal bin 'scalar' hit in coverpoint 'cp'
|
||||
%Error: t/t_covergroup_iff_illegal.v:18: Verilog $stop
|
||||
Aborting...
|
||||
Executable
+21
@@ -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('vlt_all')
|
||||
|
||||
test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(fails=True,
|
||||
check_finished=False,
|
||||
all_run_flags=['+value=5'],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,37 @@
|
||||
// DESCRIPTION: Verilator: Verilog Test module
|
||||
//
|
||||
// This file ONLY is placed under the Creative Commons Public Domain.
|
||||
// SPDX-FileCopyrightText: 2026 Wilson Snyder
|
||||
// SPDX-License-Identifier: CC0-1.0
|
||||
|
||||
// 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);
|
||||
// verilog_format: on
|
||||
|
||||
module t;
|
||||
int sample_value;
|
||||
|
||||
covergroup cg with function sample (bit [2:0] value, bit point_enable, bit bin_enable);
|
||||
cp: coverpoint value iff (point_enable) {
|
||||
bins good = {4};
|
||||
illegal_bins scalar = {5} iff (bin_enable);
|
||||
illegal_bins values[] = {0, 1} iff (bin_enable);
|
||||
wildcard illegal_bins wild = {3'b11?} iff (bin_enable);
|
||||
}
|
||||
endgroup
|
||||
|
||||
cg inst = new;
|
||||
|
||||
initial begin
|
||||
if (!$value$plusargs("value=%d", sample_value)) `stop;
|
||||
for (int i = 0; i < 8; ++i) inst.sample(3'(i), 1, 0);
|
||||
for (int i = 0; i < 8; ++i) inst.sample(3'(i), 0, 1);
|
||||
`ifdef VERILATOR
|
||||
`checkd($c32("Verilated::threadContextp()->errorCount()"), 0);
|
||||
`endif
|
||||
$display("Disabled guards produced no illegal-bin errors.");
|
||||
inst.sample(3'(sample_value), 1, 1);
|
||||
$finish;
|
||||
end
|
||||
endmodule
|
||||
@@ -0,0 +1,4 @@
|
||||
Disabled guards produced no illegal-bin errors.
|
||||
[0] %Error: t_covergroup_iff_illegal.v:19: Assertion failed in top.t.cg.sample: Illegal bin 'values' hit in coverpoint 'cp'
|
||||
%Error: t/t_covergroup_iff_illegal.v:19: Verilog $stop
|
||||
Aborting...
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/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('vlt_all')
|
||||
|
||||
test.top_filename = 't/t_covergroup_iff_illegal.v'
|
||||
|
||||
test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(fails=True,
|
||||
check_finished=False,
|
||||
all_run_flags=['+value=1'],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -0,0 +1,4 @@
|
||||
Disabled guards produced no illegal-bin errors.
|
||||
[0] %Error: t_covergroup_iff_illegal.v:20: Assertion failed in top.t.cg.sample: Illegal bin 'wild' hit in coverpoint 'cp'
|
||||
%Error: t/t_covergroup_iff_illegal.v:20: Verilog $stop
|
||||
Aborting...
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
#!/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('vlt_all')
|
||||
|
||||
test.top_filename = 't/t_covergroup_iff_illegal.v'
|
||||
|
||||
test.compile(verilator_flags2=['--coverage'], threads=(2 if test.vltmt else 1))
|
||||
|
||||
test.execute(fails=True,
|
||||
check_finished=False,
|
||||
all_run_flags=['+value=6'],
|
||||
expect_filename=test.golden_filename)
|
||||
|
||||
test.passes()
|
||||
@@ -792,7 +792,7 @@ module Vt_debug_emitv_t;
|
||||
covergroup Vt_debug_emitv_cg_basic;
|
||||
function new;
|
||||
cp_sig: coverpoint cg_sig {
|
||||
bins low = {['sh0:'sh3]};
|
||||
bins low = {['sh0:'sh3]} iff (cg_sig2[0]);
|
||||
bins high = {['sh4:'sh6]};
|
||||
bins multi = {'sh0, 'sh1, 'sh2};
|
||||
bins dflt = default;
|
||||
|
||||
@@ -395,7 +395,7 @@ module t (/*AUTOARG*/
|
||||
option.per_instance = 1;
|
||||
option.weight = 2;
|
||||
cp_sig: coverpoint cg_sig {
|
||||
bins low = {[0:3]};
|
||||
bins low = {[0:3]} iff (cg_sig2[0]);
|
||||
bins high = {[4:6]};
|
||||
bins multi = {0, 1, 2}; // multiple values in one bins (exercises EmitV range loop)
|
||||
bins dflt = default;
|
||||
|
||||
Reference in New Issue
Block a user