2026-06-05 15:35:01 +02:00
// DESCRIPTION: Verilator: Verilog Test module
//
// Tests bin counts, mixed bin types (single values, lists, ranges), and
// coverage database format (verifies coverage.dat contains covergroup entries).
//
// This file ONLY is placed under the Creative Commons Public Domain.
// SPDX-FileCopyrightText: 2026 Matthew Ballance
// SPDX-License-Identifier: CC0-1.0
// 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 [ 3 : 0 ] data ;
logic [ 7 : 0 ] opcode ;
logic signed [ 3 : 0 ] sdata ;
typedef struct packed { bit [ 7 : 0 ] value ; } f_t ;
f_t f1 ;
// cg: basic bin count tracking
covergroup cg ;
2026-06-06 00:36:55 +02:00
coverpoint data { bins zero = { 0 } ; bins low = { [ 1 : 3 ] } ; }
2026-06-05 15:35:01 +02:00
endgroup
// cg_mixed: mixed bin types - single values, multi-value lists, ranges
covergroup cg_mixed ;
coverpoint opcode {
2026-06-06 00:36:55 +02:00
bins nop = { 8 'h00 } ;
bins load = { 8 'h01 , 8 'h02 , 8 'h03 } ;
2026-06-05 15:35:01 +02:00
bins store = { 8 'h04 , 8 'h05 } ;
2026-06-06 00:36:55 +02:00
bins arith = { [ 8 'h10 : 8 'h1F ] } ;
bins other = { [ 8 'h20 : 8 'hFE ] } ;
2026-06-05 15:35:01 +02:00
}
endgroup
// cg_db: labeled coverpoint - verifies the coverage database records the correct hierarchy path
covergroup cg_db ;
2026-06-06 00:36:55 +02:00
cp: coverpoint data { bins low = { [ 0 : 3 ] } ; bins high = { [ 8 : 15 ] } ; }
2026-06-05 15:35:01 +02:00
endgroup
// cg_unbounded: open-ended bin range - '$' resolves to the coverpoint domain max (15 for 4-bit)
covergroup cg_unbounded ;
cp: coverpoint data {
2026-06-06 00:36:55 +02:00
bins lo = { [ 0 : 9 ] } ; // 1 bin: values 0..9
bins others = { [ 10 : $ ] } ; // 1 bin: values 10..15 ($ == domain max)
2026-06-05 15:35:01 +02:00
}
endgroup
// cg_unbounded_lo: open-ended LOWER bound - '$' resolves to the domain min (0)
covergroup cg_unbounded_lo ;
cp: coverpoint data {
2026-06-06 00:36:55 +02:00
bins start = { [ $ : 5 ] } ; // 1 bin: values 0..5 ($ == domain min) -> expr <= 5
bins rest = { [ 6 : 15 ] } ;
2026-06-05 15:35:01 +02:00
}
endgroup
// cg_unbounded_all: both bounds open ('[$:$]' covers the entire domain -> always true)
covergroup cg_unbounded_all ;
cp: coverpoint data {
2026-06-06 00:36:55 +02:00
bins all = { [ $ : $ ] } ; // 1 bin: every value
2026-06-05 15:35:01 +02:00
}
endgroup
// cg_unbounded_signed: signed coverpoint with open-ended bounds in both directions
covergroup cg_unbounded_signed ;
cp: coverpoint sdata {
2026-06-06 00:36:55 +02:00
bins hi = { [ 2 : $ ] } ; // sdata >= 2 (signed >=)
bins lo = { [ $ : - 2 ] } ; // sdata <= -2 (signed <=)
2026-06-05 15:35:01 +02:00
}
endgroup
// cg_sel: coverpoint over a struct-member part-select expression (AstSel)
covergroup cg_sel ;
cp: coverpoint f1 . value [ 3 : 0 ] { // low nibble only; upper bits ignored
2026-06-06 00:36:55 +02:00
bins lo = { [ 0 : 7 ] } ; bins hi = { [ 8 : 15 ] } ;
2026-06-05 15:35:01 +02:00
}
endgroup
2026-06-06 00:36:55 +02:00
cg cg_inst ;
cg_mixed cg_mixed_inst ;
cg_db cg_db_inst ;
cg_unbounded cg_unbounded_inst ;
cg_unbounded_lo cg_unbounded_lo_inst ;
cg_unbounded_all cg_unbounded_all_inst ;
2026-06-05 15:35:01 +02:00
cg_unbounded_signed cg_unbounded_signed_inst ;
2026-06-06 00:36:55 +02:00
cg_sel cg_sel_inst ;
2026-06-05 15:35:01 +02:00
initial begin
2026-06-06 00:36:55 +02:00
cg_inst = new ;
cg_mixed_inst = new ;
cg_db_inst = new ;
cg_unbounded_inst = new ;
cg_unbounded_lo_inst = new ;
cg_unbounded_all_inst = new ;
2026-06-05 15:35:01 +02:00
cg_unbounded_signed_inst = new ;
2026-06-06 00:36:55 +02:00
cg_sel_inst = new ;
2026-06-05 15:35:01 +02:00
2026-06-06 00:36:55 +02:00
data = 0 ;
cg_inst . sample ( ) ; // zero: 1
2026-06-05 15:35:01 +02:00
`checkr ( cg_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
data = 1 ;
cg_inst . sample ( ) ; // low: 1
2026-06-05 15:35:01 +02:00
`checkr ( cg_inst . get_inst_coverage ( ) , 100.0 ) ;
2026-06-06 00:36:55 +02:00
data = 2 ;
cg_inst . sample ( ) ; // low: 2
data = 2 ;
cg_inst . sample ( ) ; // low: 3
2026-06-05 15:35:01 +02:00
2026-06-06 00:36:55 +02:00
opcode = 8 'h00 ;
cg_mixed_inst . sample ( ) ; // nop
2026-06-05 15:35:01 +02:00
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 20.0 ) ;
2026-06-06 00:36:55 +02:00
opcode = 8 'h02 ;
cg_mixed_inst . sample ( ) ; // load
2026-06-05 15:35:01 +02:00
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 40.0 ) ;
2026-06-06 00:36:55 +02:00
opcode = 8 'h05 ;
cg_mixed_inst . sample ( ) ; // store
2026-06-05 15:35:01 +02:00
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 60.0 ) ;
2026-06-06 00:36:55 +02:00
opcode = 8 'h15 ;
cg_mixed_inst . sample ( ) ; // arith
2026-06-05 15:35:01 +02:00
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 80.0 ) ;
2026-06-06 00:36:55 +02:00
opcode = 8 'h80 ;
cg_mixed_inst . sample ( ) ; // other
2026-06-05 15:35:01 +02:00
`checkr ( cg_mixed_inst . get_inst_coverage ( ) , 100.0 ) ;
2026-06-06 00:36:55 +02:00
data = 1 ;
cg_db_inst . sample ( ) ; // low
2026-06-05 15:35:01 +02:00
`checkr ( cg_db_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
data = 10 ;
cg_db_inst . sample ( ) ; // high
2026-06-05 15:35:01 +02:00
`checkr ( cg_db_inst . get_inst_coverage ( ) , 100.0 ) ;
// Open-ended range: '$' upper bound covers values up to the domain max
2026-06-06 00:36:55 +02:00
data = 5 ;
cg_unbounded_inst . sample ( ) ; // lo
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
data = 12 ;
cg_unbounded_inst . sample ( ) ; // others ([10:$] covers 12)
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_inst . get_inst_coverage ( ) , 100.0 ) ;
// Open-ended lower bound: '$' min covers values down to 0
2026-06-06 00:36:55 +02:00
data = 3 ;
cg_unbounded_lo_inst . sample ( ) ; // start ([$:5] covers 3)
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_lo_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
data = 10 ;
cg_unbounded_lo_inst . sample ( ) ; // rest
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_lo_inst . get_inst_coverage ( ) , 100.0 ) ;
// Both-open range '[$:$]' matches any value
2026-06-06 00:36:55 +02:00
data = 7 ;
cg_unbounded_all_inst . sample ( ) ; // all
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_all_inst . get_inst_coverage ( ) , 100.0 ) ;
// Signed open-ended bounds: '$' resolves to signed domain max/min
2026-06-06 00:36:55 +02:00
sdata = 5 ;
cg_unbounded_signed_inst . sample ( ) ; // hi ([2:$] covers 5)
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_signed_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
sdata = - 5 ;
cg_unbounded_signed_inst . sample ( ) ; // lo ([$:-2] covers -5)
2026-06-05 15:35:01 +02:00
`checkr ( cg_unbounded_signed_inst . get_inst_coverage ( ) , 100.0 ) ;
// Part-select coverpoint: only the low nibble is sampled (upper bits ignored)
2026-06-06 00:36:55 +02:00
f1 . value = 8 'h03 ;
cg_sel_inst . sample ( ) ; // nibble 3 -> lo
2026-06-05 15:35:01 +02:00
`checkr ( cg_sel_inst . get_inst_coverage ( ) , 50.0 ) ;
2026-06-06 00:36:55 +02:00
f1 . value = 8 'hF9 ;
cg_sel_inst . sample ( ) ; // nibble 9 -> hi (upper bits F ignored)
2026-06-05 15:35:01 +02:00
`checkr ( cg_sel_inst . get_inst_coverage ( ) , 100.0 ) ;
$write ( " *-* All Finished *-* \n " ) ;
$finish ;
end
endmodule