// 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; typedef struct packed {logic m_p; logic h_mode;} cfg_t; cfg_t s_cfg = '0; // 2-way cross covergroup cg2; cp_addr: coverpoint addr {bins addr0 = {0}; bins addr1 = {1};} cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};} addr_cmd: cross cp_addr, cp_cmd; endgroup // 3-way cross covergroup cg3; 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};} addr_cmd_mode: cross cp_addr, cp_cmd, cp_mode; endgroup // 4-way cross covergroup cg4; 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};} addr_cmd_mode_parity: cross cp_addr, cp_cmd, cp_mode, cp_parity; endgroup // Cross with option set inside the cross body covergroup cg5; 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;} endgroup // 2-way cross where one coverpoint uses a range bin covergroup cg_range; cp_addr: coverpoint addr { bins lo_range = {[0 : 1]}; // range bin bins hi_range = {[2 : 3]}; } cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};} 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}; } cp_cmd: coverpoint cmd {bins read = {0}; bins write = {1};} cross_ab: cross cp_addr, cp_cmd; endgroup // Cross with option.at_least set in the cross body covergroup cg_at_least; 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;} endgroup // Cross with option.goal set in the cross body covergroup cg_goal; 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;} endgroup // Cross with an unsupported option (option.per_instance) - Verilator warns and ignores it covergroup cg_unsup_cross_opt; 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{ option.per_instance = 1; // unsupported for cross - expect COVERIGN warning } // 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 */ endgroup // Covergroup with an unnamed cross - the cross is reported under the default name "cross" covergroup cg_unnamed_cross; cp_a: coverpoint addr {bins a0 = {0}; bins a1 = {1};} cp_c: coverpoint cmd {bins read = {0}; bins write = {1};} cross cp_a, cp_c; // no label: reported under the default cross name endgroup // 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 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; cg_mixed cg_mixed_inst = new; cg_def_cross cg_def_cross_inst = new; initial begin // Sample 2-way: hit all 4 combinations // cg2: 2 cp bins + 2 cp bins + 4 cross bins = 8 bins total (flat count) addr = 0; cmd = 0; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x read `checkr(cg2_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read addr = 1; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr1 x write `checkr(cg2_inst.get_inst_coverage(), 75.0); // 6/8: all cp bins + 2 cross bins addr = 0; cmd = 1; mode = 0; parity = 0; cg2_inst.sample(); // addr0 x write `checkr(cg2_inst.get_inst_coverage(), 87.5); // 7/8: 3 cross bins hit 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 // 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) 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 // Sample 4-way: hit 4 of 16 combinations // cg4: 2+2+2+2+16=24 bins; 4 cross bins hit -> 12/24=50% addr = 0; cmd = 0; mode = 0; parity = 0; cg4_inst.sample(); addr = 1; cmd = 1; mode = 0; parity = 1; cg4_inst.sample(); `checkr(cg4_inst.get_inst_coverage(), 37.5); // 9/24: all cp bins + 2 cross bins addr = 0; cmd = 1; mode = 1; parity = 0; cg4_inst.sample(); addr = 1; cmd = 0; mode = 1; parity = 1; cg4_inst.sample(); `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% addr = 0; cmd = 0; cg5_inst.sample(); `checkr(cg5_inst.get_inst_coverage(), 37.5); // 3/8: addr0, read, addr0_x_read addr = 1; cmd = 1; cg5_inst.sample(); `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 addr = 0; cmd = 0; cg_ignore_inst.sample(); // a0 x read `checkr(cg_ignore_inst.get_inst_coverage(), 37.5); // 3/8 addr = 1; cmd = 1; cg_ignore_inst.sample(); // a1 x write `checkr(cg_ignore_inst.get_inst_coverage(), 75.0); // 6/8 addr = 0; cmd = 1; cg_ignore_inst.sample(); // a0 x write `checkr(cg_ignore_inst.get_inst_coverage(), 87.5); // 7/8 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% // Sample range-bin cross // cg_range: 2+2+4=8 bins addr = 0; cmd = 0; cg_range_inst.sample(); // lo_range x read `checkr(cg_range_inst.get_inst_coverage(), 37.5); // 3/8 addr = 2; cmd = 1; cg_range_inst.sample(); // hi_range x write `checkr(cg_range_inst.get_inst_coverage(), 75.0); // 6/8 addr = 1; cmd = 1; cg_range_inst.sample(); // lo_range x write `checkr(cg_range_inst.get_inst_coverage(), 87.5); // 7/8 addr = 3; cmd = 0; cg_range_inst.sample(); // hi_range x read `checkr(cg_range_inst.get_inst_coverage(), 100.0); // 8/8 // 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% addr = 0; cmd = 0; cg_at_least_inst.sample(); // addr0 x read addr = 1; cmd = 1; cg_at_least_inst.sample(); // addr1 x write `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% addr = 0; cmd = 0; cg_goal_inst.sample(); // addr0 x read addr = 1; cmd = 1; cg_goal_inst.sample(); // addr1 x write `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% 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 `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% addr = 0; cmd = 0; cg_unnamed_cross_inst.sample(); // a0 x read addr = 1; cmd = 1; cg_unnamed_cross_inst.sample(); // a1 x write `checkr(cg_unnamed_cross_inst.get_inst_coverage(), 75.0); // 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 $write("*-* All Finished *-*\n"); $finish; end endmodule