2026-06-05 15:35:01 +02:00
// 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
// Test cross coverage: 2-way, 3-way, and 4-way crosses
// verilog_format: off
`define stop $stop
`define checkr(gotv,expv) do if ((gotv) != (expv)) begin $write("%%Error: %s:%0d: got=%f exp=%f\n", `__FILE__,`__LINE__, (gotv), (expv)); `stop; end while(0);
// verilog_format: on
module t ;
logic [ 1 : 0 ] addr ;
logic cmd ;
logic mode ;
logic parity ;
2026-06-24 13:47:36 +02:00
typedef struct packed { logic m_p ; logic h_mode ; } cfg_t ;
cfg_t s_cfg = '0 ;
2026-06-05 15:35:01 +02:00
// 2-way cross
covergroup cg2 ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
2026-06-05 15:35:01 +02:00
addr_cmd: cross cp_addr , cp_cmd ;
endgroup
// 3-way cross
covergroup cg3 ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; bins addr2 = { 2 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
cp_mode: coverpoint mode { bins normal = { 0 } ; bins debug = { 1 } ; }
2026-06-05 15:35:01 +02:00
addr_cmd_mode: cross cp_addr , cp_cmd , cp_mode ;
endgroup
// 4-way cross
covergroup cg4 ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
cp_mode: coverpoint mode { bins normal = { 0 } ; bins debug = { 1 } ; }
cp_parity: coverpoint parity { bins even = { 0 } ; bins odd = { 1 } ; }
2026-06-05 15:35:01 +02:00
addr_cmd_mode_parity: cross cp_addr , cp_cmd , cp_mode , cp_parity ;
endgroup
// Cross with option set inside the cross body
covergroup cg5 ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
addr_cmd_opt: cross cp_addr , cp_cmd { option . weight = 2 ; }
2026-06-05 15:35:01 +02:00
endgroup
// 2-way cross where one coverpoint uses a range bin
covergroup cg_range ;
cp_addr: coverpoint addr {
2026-06-06 00:36:55 +02:00
bins lo_range = { [ 0 : 1 ] } ; // range bin
bins hi_range = { [ 2 : 3 ] } ;
2026-06-05 15:35:01 +02:00
}
2026-06-06 00:36:55 +02:00
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
2026-06-05 15:35:01 +02:00
addr_cmd_range: cross cp_addr , cp_cmd ;
endgroup
// Cross where one coverpoint has ignore_bins - ignored values must not appear in cross bins
covergroup cg_ignore ;
cp_addr: coverpoint addr {
ignore_bins ign = { 3 } ; // addr=3 excluded from cross
bins a0 = { 0 } ;
bins a1 = { 1 } ;
}
2026-06-06 00:36:55 +02:00
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
2026-06-05 15:35:01 +02:00
cross_ab: cross cp_addr , cp_cmd ;
endgroup
// Cross with option.at_least set in the cross body
covergroup cg_at_least ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
addr_cmd_al: cross cp_addr , cp_cmd { option . at_least = 3 ; }
2026-06-05 15:35:01 +02:00
endgroup
// Cross with option.goal set in the cross body
covergroup cg_goal ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
addr_cmd_goal: cross cp_addr , cp_cmd { option . goal = 90 ; }
2026-06-05 15:35:01 +02:00
endgroup
// Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it
covergroup cg_unsup_cross_opt ;
2026-06-06 00:36:55 +02:00
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
addr_cmd_unsup: cross cp_addr , cp_cmd {
2026-06-05 15:35:01 +02:00
option . per_instance = 1 ; // unsupported for cross - expect COVERIGN warning
}
2026-06-24 13:47:36 +02:00
// Non-standard hierarchical reference as a cross item (an implicit coverpoint):
// accepted with NONSTD, but implicit coverpoints are unsupported so the whole
// cross is dropped (COVERIGN, suppressed here) - it contributes no bins.
/* verilator lint_off NONSTD */
cross_hier: cross cp_addr , s_cfg . m_p ;
/* verilator lint_on NONSTD */
2026-06-05 15:35:01 +02:00
endgroup
// Covergroup with an unnamed cross - the cross is reported under the default name "cross"
covergroup cg_unnamed_cross ;
2026-06-06 00:36:55 +02:00
cp_a: coverpoint addr { bins a0 = { 0 } ; bins a1 = { 1 } ; }
cp_c: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
2026-06-05 15:35:01 +02:00
cross cp_a , cp_c ; // no label: reported under the default cross name
endgroup
2026-06-12 17:40:48 +02:00
// Cross plus an un-crossed coverpoint: get_inst_coverage must combine the converted
// (VlCoverpoint) coverpoint cp_solo with the legacy cross/crossed-coverpoint bins.
covergroup cg_mixed ;
cp_addr: coverpoint addr { bins addr0 = { 0 } ; bins addr1 = { 1 } ; }
cp_cmd: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
cp_solo: coverpoint mode { bins normal = { 0 } ; bins debug = { 1 } ; } // not crossed
ab: cross cp_addr , cp_cmd ;
endgroup
// Crossed (hence non-convertible) coverpoint that also has a default bin: exercises the
// legacy default-bin codegen path that converted coverpoints bypass.
covergroup cg_def_cross ;
cp_a: coverpoint addr iff ( mode ) { bins a0 = { 0 } ; bins a1 = { 1 } ; bins ad = default ; }
cp_c: coverpoint cmd { bins read = { 0 } ; bins write = { 1 } ; }
axc: cross cp_a , cp_c ;
endgroup
2026-06-05 15:35:01 +02:00
cg2 cg2_inst = new ;
cg_ignore cg_ignore_inst = new ;
cg_range cg_range_inst = new ;
cg3 cg3_inst = new ;
cg4 cg4_inst = new ;
cg5 cg5_inst = new ;
cg_at_least cg_at_least_inst = new ;
cg_goal cg_goal_inst = new ;
cg_unsup_cross_opt cg_unsup_cross_opt_inst = new ;
cg_unnamed_cross cg_unnamed_cross_inst = new ;
2026-06-12 17:40:48 +02:00
cg_mixed cg_mixed_inst = new ;
cg_def_cross cg_def_cross_inst = new ;
2026-06-05 15:35:01 +02:00
initial begin
// Sample 2-way: hit all 4 combinations
// cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count)
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
mode = 0 ;
parity = 0 ;
cg2_inst . sample ( ) ; // addr0 x read
2026-06-05 15:35:01 +02:00
`checkr ( cg2_inst . get_inst_coverage ( ) , 37.5 ) ; // 3/8: addr0, read, addr0_x_read
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 1 ;
mode = 0 ;
parity = 0 ;
cg2_inst . sample ( ) ; // addr1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg2_inst . get_inst_coverage ( ) , 75.0 ) ; // 6/8: all cp bins + 2 cross bins
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 1 ;
mode = 0 ;
parity = 0 ;
cg2_inst . sample ( ) ; // addr0 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg2_inst . get_inst_coverage ( ) , 87.5 ) ; // 7/8: 3 cross bins hit
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 0 ;
mode = 0 ;
parity = 0 ;
cg2_inst . sample ( ) ; // addr1 x read
`checkr ( cg2_inst . get_inst_coverage ( ) , 100.0 ) ; // 8/8: all 4 cross bins hit
2026-06-05 15:35:01 +02:00
// Sample 3-way: hit 4 of 12 combinations
// cg3: 3+2+2+12=19 bins; 4 cross bins hit -> 11/19=57.9% (not clean; no intermediate checkr)
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
mode = 0 ;
cg3_inst . sample ( ) ; // addr0 x read x normal
addr = 1 ;
cmd = 1 ;
mode = 0 ;
cg3_inst . sample ( ) ; // addr1 x write x normal
addr = 2 ;
cmd = 0 ;
mode = 1 ;
cg3_inst . sample ( ) ; // addr2 x read x debug
addr = 0 ;
cmd = 1 ;
mode = 1 ;
cg3_inst . sample ( ) ; // addr0 x write x debug
2026-06-05 15:35:01 +02:00
// Sample 4-way: hit 4 of 16 combinations
// cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
mode = 0 ;
parity = 0 ;
cg4_inst . sample ( ) ;
addr = 1 ;
cmd = 1 ;
mode = 0 ;
parity = 1 ;
cg4_inst . sample ( ) ;
2026-06-05 15:35:01 +02:00
`checkr ( cg4_inst . get_inst_coverage ( ) , 37.5 ) ; // 9/24: all cp bins + 2 cross bins
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 1 ;
mode = 1 ;
parity = 0 ;
cg4_inst . sample ( ) ;
addr = 1 ;
cmd = 0 ;
mode = 1 ;
parity = 1 ;
cg4_inst . sample ( ) ;
2026-06-05 15:35:01 +02:00
`checkr ( cg4_inst . get_inst_coverage ( ) , 50.0 ) ; // 12/24: all cp bins + 4 cross bins
// Sample cg5 (cross with option.weight=2; weight is ignored in flat bin count)
// cg5: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg5_inst . sample ( ) ;
2026-06-05 15:35:01 +02:00
`checkr ( cg5_inst . get_inst_coverage ( ) , 37.5 ) ; // 3/8: addr0, read, addr0_x_read
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 1 ;
cg5_inst . sample ( ) ;
2026-06-05 15:35:01 +02:00
`checkr ( cg5_inst . get_inst_coverage ( ) , 75.0 ) ; // 6/8: all cp bins + 2 cross bins
// Sample cg_ignore: addr=3 is in ignore_bins so no cross bins for it
// cg_ignore: 2+2+4=8 bins total
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_ignore_inst . sample ( ) ; // a0 x read
2026-06-05 15:35:01 +02:00
`checkr ( cg_ignore_inst . get_inst_coverage ( ) , 37.5 ) ; // 3/8
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 1 ;
cg_ignore_inst . sample ( ) ; // a1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_ignore_inst . get_inst_coverage ( ) , 75.0 ) ; // 6/8
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 1 ;
cg_ignore_inst . sample ( ) ; // a0 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_ignore_inst . get_inst_coverage ( ) , 87.5 ) ; // 7/8
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 0 ;
cg_ignore_inst . sample ( ) ; // a1 x read
`checkr ( cg_ignore_inst . get_inst_coverage ( ) , 100.0 ) ; // 8/8
addr = 3 ;
cmd = 0 ;
cg_ignore_inst . sample ( ) ; // ignored (addr=3 in ignore_bins)
`checkr ( cg_ignore_inst . get_inst_coverage ( ) , 100.0 ) ; // still 100%
2026-06-05 15:35:01 +02:00
// Sample range-bin cross
// cg_range: 2+2+4=8 bins
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_range_inst . sample ( ) ; // lo_range x read
2026-06-05 15:35:01 +02:00
`checkr ( cg_range_inst . get_inst_coverage ( ) , 37.5 ) ; // 3/8
2026-06-06 00:36:55 +02:00
addr = 2 ;
cmd = 1 ;
cg_range_inst . sample ( ) ; // hi_range x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_range_inst . get_inst_coverage ( ) , 75.0 ) ; // 6/8
2026-06-06 00:36:55 +02:00
addr = 1 ;
cmd = 1 ;
cg_range_inst . sample ( ) ; // lo_range x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_range_inst . get_inst_coverage ( ) , 87.5 ) ; // 7/8
2026-06-06 00:36:55 +02:00
addr = 3 ;
cmd = 0 ;
cg_range_inst . sample ( ) ; // hi_range x read
`checkr ( cg_range_inst . get_inst_coverage ( ) , 100.0 ) ; // 8/8
2026-06-05 15:35:01 +02:00
// Sample cg_at_least (option.at_least in cross body; Verilator uses at_least=1 for bins)
// cg_at_least: 2+2+4=8 bins; 2 cross bins hit (count=1, at_least effectively 1) -> 6/8=75%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_at_least_inst . sample ( ) ; // addr0 x read
addr = 1 ;
cmd = 1 ;
cg_at_least_inst . sample ( ) ; // addr1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_at_least_inst . get_inst_coverage ( ) , 75.0 ) ;
// Sample cg_goal (option.goal in cross body; does not affect hit counting)
// cg_goal: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_goal_inst . sample ( ) ; // addr0 x read
addr = 1 ;
cmd = 1 ;
cg_goal_inst . sample ( ) ; // addr1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_goal_inst . get_inst_coverage ( ) , 75.0 ) ;
// Sample cg_unsup_cross_opt
// cg_unsup_cross_opt: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_unsup_cross_opt_inst . sample ( ) ; // addr0 x read
addr = 1 ;
cmd = 1 ;
cg_unsup_cross_opt_inst . sample ( ) ; // addr1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_unsup_cross_opt_inst . get_inst_coverage ( ) , 75.0 ) ;
// Sample cg_unnamed_cross
// cg_unnamed_cross: 2+2+4=8 bins; 2 cross bins hit -> 6/8=75%
2026-06-06 00:36:55 +02:00
addr = 0 ;
cmd = 0 ;
cg_unnamed_cross_inst . sample ( ) ; // a0 x read
addr = 1 ;
cmd = 1 ;
cg_unnamed_cross_inst . sample ( ) ; // a1 x write
2026-06-05 15:35:01 +02:00
`checkr ( cg_unnamed_cross_inst . get_inst_coverage ( ) , 75.0 ) ;
2026-06-12 17:40:48 +02:00
// Sample cg_mixed: 10 bins total (cp_addr 2 + cp_cmd 2 + cp_solo 2 + cross ab 4)
addr = 0 ; cmd = 0 ; mode = 0 ;
cg_mixed_inst . sample ( ) ; // addr0, read, solo normal, ab(addr0_x_read)
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 40.0 ) ; // 4/10
addr = 0 ; cmd = 1 ; mode = 1 ;
cg_mixed_inst . sample ( ) ; // addr0, write, solo debug, ab(addr0_x_write)
addr = 1 ; cmd = 0 ; mode = 0 ;
cg_mixed_inst . sample ( ) ; // addr1, read, ab(addr1_x_read)
addr = 1 ; cmd = 1 ; mode = 1 ;
cg_mixed_inst . sample ( ) ; // addr1, write, ab(addr1_x_write)
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 100.0 ) ; // 10/10
// Sample cg_def_cross (default bin in a crossed coverpoint, gated by iff)
mode = 1 ;
addr = 0 ; cmd = 0 ; cg_def_cross_inst . sample ( ) ; // a0, read
addr = 2 ; cmd = 1 ; cg_def_cross_inst . sample ( ) ; // ad (default), write
2026-06-05 15:35:01 +02:00
$write ( " *-* All Finished *-* \n " ) ;
$finish ;
end
endmodule