As you probably already know, all digital design circuits either process or transfer data, which is usually represented as a bit vector of size N. Data values that pass through the system provide an indication of how system’s functionality is exercised, so you need to add them to the functional coverage goals. You might ask yourself: “What are relevant data values should I cover?” and “Are there any relevant bit relation/patterns should I cover?” These questions are answered by means of bitwise relationships, the most important of which you’ll find in this article.

We are going to look at following bitwise coverage methods:

- Bit Toggle Coverage
- Walking-1 or Walking-0 Coverage
- Power-of-Two Coverage
- Alignment Coverage
- Duty Cycle Coverage
- Parity Coverage
- Consecutive Bit Coverage
- Bit Masking Coverage

## Bit Toggle Coverage

__Usage__

- To indicate there is activity on a bus (e.g. during the smoke-testing phase)

Bit Toggle Coverage ensures that all the bits on the bus have toggled at least once, regardless of the relationships between toggling bits. In general, it is used to show there is activity on the bus, but does not indicate the diversity of bit toggling. A sequence that is “all-0s followed by all-1s followed by all-0s” will fill it up, as the picture below shows.

Bit Toggle Coverage can be enabled as part of the code coverage collection or implemented as functional coverage as the following code shows.

```
class bit_toggle_cg_wrapper; // covergroup wrapper class
covergroup bit_toggle_cg(input int bit_idx) with function sample(bit x, int aidx);
bit_transition: coverpoint x iff (bit_idx == aidx) {
bins zeroone = (0 => 1);
bins onezero = (1 => 0);
}
endgroup
function new(string name="bit_toggle_cg_wrapper", int aidx=0);
bit_toggle_cg = new(aidx);
bit_toggle_cg.set_inst_name(name);
endfunction
function void sample(bit x, int aidx);
bit_toggle_cg.sample(x, aidx);
endfunction
endclass
bit_toggle_cg_wrapper bit_toggle_cg_w[WIDTH];
// NOTE: bit_toggle_cg_w needs to be created using new("
```") before calling sample_bit_toggle
function void sample_bit_toggle(bit[WIDTH-1:0] x);
for(int i=0;i<WIDTH;i++)
bit_toggle_cg_w[i].sample(x, i);
endfunction

__Pros__

- easy to implement
- easy to cover (2*N coverage bins)

__Cons__

- not reliable: it can miss cross-connected signals
- does not highlight relationships between toggling bits
- limited to integers/bit-vectors: although most code coverage collection engines support this method, it can not be used for abstract values (e.g. fields, enumerated types)

## Walking-1 or Walking-0 Coverage

__Usage__

- To measure activity covered by bus connectivity tests
- To measure one-hot state or bus encoding coverage

Walking-1 Coverage samples the cases in which only one bit is set while others remain 0 (one-hot encoding):

Code coverage engines do not support this type of coverage and must be implemented as functional coverage:

```
covergroup walking_1_cg with function sample(bit[WIDTH-1:0] x, int position);
walking_1: coverpoint position iff (x[position]==1 && $onehot(x) ) {
bins b[] = {[0:WIDTH-1]};
}
endgroup
function void sample_walking_1(bit[WIDTH-1:0] x);
for(int i=0;i<WIDTH;i++)begin
walking_1_cg.sample(x, i);
end
endfunction
```

Walking-0 coverage is complementary to walking-1 coverage: all bits are set except for one, which is zero (i.e. one-cold). It can be implemented in a similar manner to walking-1 coverage, as follows:

```
covergroup walking_0_cg with function sample(bit[WIDTH-1:0] x, int position);
walking_0: coverpoint position iff (x[position]==0 && $onehot(~x) ) {
bins b[] = {[0:WIDTH-1]};
}
endgroup
```

__Pros__

- easy to implement
- easy to cover (N coverage bins)
- ensures each bit was toggled individually and is the only solution for one-hot or zero-hot encoded values

__Cons__

- extra effort required to steer generation: it is hard to cover using unconstrained or loosely constrained random generation
- reduced value space equivalent equates to reduced data diversity, meaning interesting values for the system may not be covered

## Power-of-Two Coverage

__Usage__

- To measure activity on buses for which value ranges have no associated semantics (e.g. RAM address/data buses)

Power-of-Two Coverage associates coverage bins with value ranges that are delimited by powers of two. If we take an 8-bit example, the following ranges will be created:

```
covergroup power_of_2_cg with function sample(bit[WIDTH-1:0] x, int position);
power_of_two: coverpoint position iff (x[position]==1 && ((x&(~((1<<(position+1))-1)))==0)) {
bins b[] = {[0:WIDTH-1]};
}
endgroup
function void sample_power_of_2(bit[WIDTH-1:0] x);
for(int i=0;i<WIDTH;i++) begin
power_of_2_cg.sample(x, i);
end
endfunction
```

Each coverage bin guarantees that the most significant bits are '0', the bit of interest is '1' and the less significant bits have random values (e.g. **8'b001?????**, **8'b000001??** etc.). This kind of value space partitioning creates uneven coverage bins: the interval corresponding to **8'b000001??** contains 4 values (i.e. [4:7]), while **8'b001?????** contains 32 values (i.e. [32:63]). This translates into a lower probability of generating and, implicitly, covering values from lower order intervals. But this is not an issue since you can use Probabilistic Distribution Functions to get around it. The *e*-Language version of distribution based constraints can be found in the article entitled Coverage Aware Generation using e Language Normal Distribution Constraints.

__Pros__

- ensures that each bit has been 1 irrespective of the values of the least significant bits
- only N coverage bins to cover

__Cons__

- None

## Alignment Coverage

__Usage__

- To measure values used within an N-bit/byte aligned context

Alignment Coverage indicates a value's alignment to a given constant. The condition that indicates alignment is **value%N == 0**, where **%** is the modulo operation and **N** is the alignment constant. The result of **value%N** falls within the [0:N-1], which gives us N values or coverage bins.

Let's consider the case of a memory with 2 buses: an internal and an external one. The internal bus is a 4-byte aligned address bus and the external bus is byte aligned. For this case **N=4** and you should fill up 4 coverage bins: [0,1,2,3].

You can implement this type of coverage as follows:

```
covergroup alignment_cg(input int align) with function sample(bit[WIDTH-1:0] x);
alignment: coverpoint (x%align) {
bins b[] = {[0:align-1]};
}
endgroup
function void sample_alignment(bit[WIDTH-1:0] x);
alignment_cg.sample(x);
endfunction
```

With a power-of-two alignment (e.g. 2, 4, 8, etc.) you can visually check the alignment on the waveform: the least significant **x** bits, where **N=2^x**, must be zero in order to be aligned.

## Duty Cycle Coverage

__Usage__

- To measure signal activity

A duty cycle is the percentage of one period for which a signal is active. The figure below depicts one period of a signal or a pulse:

Halt or back-pressure signals should be measured because their activity indicates the level of stress a resource is under. In this case you might need to collect data over an extended time window, not just one period. The figure below shows one such case:

In both cases, duty cycle coverage requires counting the 1s within the time window and dividing this number by the bit-length for the window, as follows:

```
covergroup duty_cycle_cg with function sample(int duty_cycle);
duty_cycle: coverpoint (duty_cycle) {
bins b[10] = {[0:99]};
}
endgroup
function void sample_duty_cycle(bit[15:0] x);
int unsigned count = $countones(x), duty_cycle=0;
duty_cycle = ((count * 100 )/16);
duty_cycle_cg.sample(duty_cycle);
endfunction
```

For longer time windows it might not be efficient to calculate the duty cycle at the end of the window. In this case, you should calculate it over smaller intervals and update the duty cycle from one interval to the next. This can be achieved using code such as this:

```
int unsigned bit_count = 0;
int unsigned window_length = 0;
int unsigned duty_cycle = 0;
function void sample_duty_cycle(bit bits[$]);
bit_count += bits.sum();
window_length += bits.size();
duty_cycle = (bit_count*100)/window_length;
// sample duty_cycle when target window size is reached
if (window_length >= target_window_size)
duty_cycle_cg.sample(duty_cycle);
endfunction
```

## Parity Coverage

__Usage__

- To indicate amount of 0s or 1s contained by values

The parity of an N-bit value indicates whether the value contains an odd (odd parity) or even(even parity) number of 1-bits. Depending on the value of **N**, there will be either **N/2** or **N/2+1**coverage bins that need to be covered, as the table below shows:

N bits | Odd Parity Coverage Bins | Even Parity Coverage Bins |
---|---|---|

1 | 1 | 0 |

2 | 1 | 0,2 |

3 | 1,3 | 0,2 |

8 | 1,3,5,7 | 0,2,4,6,8 |

15 | 1,3,5,7,9,11,13,15 | 0,2,4,6,8,10,12,14 |

Whether you choose between odd or even parity depends on the application. Below is the code for the odd parity only:

```
covergroup odd_parity_cg(input int bitwidth) with function sample(int aparity);
parity: coverpoint (aparity % 2) {
bins is_even = {0};
bins is_odd = {1};
}
parity_amount: coverpoint (aparity) {
bins b[] = {[1:bitwidth]} with (item % 2 == 1);
}
endgroup
function void sample_odd_parity(bit[WIDTH-1:0] x);
int unsigned count = $countones(x);
odd_parity_cg.sample(count);
endfunction
```

You can also create a transition coverpoint to measure parity sequencing (e.g. odd => even => odd).

## Consecutive Bit Coverage

__Usage__

- To measure bit group size

There are cases, as with a 1-bit serial bus, where you need to identify and cover bit groups within a time window. Groups are N consecutive 1s or 0s and there can be more than one group within a value. Coverage of bit groups includes the size of the bit group and the number of bit groups, as can be seen from the code below:

```
covergroup consecutive_bits_cg(input int limit) with function sample(int nof_1_bits, int nof_groups);
nof_consecutive_bits: coverpoint (nof_1_bits) {
bins b[] = {[0:limit]};
}
nof_bit_groups: coverpoint (nof_groups) {
bins b[] = {[0:WIDTH/2+WIDTH%2]};
}
endgroup
function void sample_consecutive_bits(bit[WIDTH-1:0] x);
int unsigned count = 0, nof_groups = 0;
int unsigned counta[$];
if (x == 0 || x == {WIDTH{1'b1}}) begin
consecutive_bits_cg.sample((x == 0)?0:WIDTH, (x == 0)?0:1);
return;
end
for(int i=0; i<WIDTH; i++) begin
count += x[i];
if ((x[i] == 0 || (i == (WIDTH-1))) && count != 0) begin
nof_groups += 1;
counta.push_back(count);
count = 0;
end
end
foreach(counta[i])
consecutive_bits_cg.sample(counta[i], counta.size());
endfunction
```

## Bit Masking Coverage

__Usage__

- To measure the inputs into a masking function (e.g. frame filter: forward or drop Ethernet frames by MAC address)

Bit Masking Coverage indicates how a test value relates to a reference value given that the matching of the two takes into account a pattern called a mask (static, dynamic or configurable). The mask is applied to both test and reference values. The resulting masked values are compared to whether or not they are equal, as shown in the figure bellow:

Two aspects need to be clarified upfront: the mask granularity and the mask interpretation.

The mask granularity tells you how each mask bit is mapped to the bit-vector representing the values, as in the following examples:

The mask interpretation can be either a) select the bits to be compared or b) select the bits to be ignored in the comparison (i.e. the reverse of a)).

Using the above implementation, one option could be to cover the cross between bits and the masking result: **cross test_value[i], mask[i], reference_value[2], masking result**, where **i** is between 0 and the bit-width of the values. Another option is to cover a reduced set of mask values cross with masking result; the reduced set of mask values could be all-0s, all-1s or walking-1. An example implementation of the second option is given below:

```
covergroup masking_cg with function sample(bit[WIDTH-1:0] x, int position, bit amasking_result);
mask: coverpoint position iff (x == 0 || x == {WIDTH{1'b1}} || ($onehot(x) && x[position-1] == 1) ) {
bins zero = {0};
bins w1[] = {[1:WIDTH]};
bins all1 = {WIDTH + 1};
}
masking_result : coverpoint amasking_result {
bins no_match = {0};
bins match = {1};
}
mask_vs_result : cross mask, masking_result {
ignore_bins all_pass = binsof(mask) intersect {0} && binsof(masking_result) intersect {0};
}
endgroup
function void sample_mask(bit[WIDTH-1:0] x, bit masking_result);
if (x == 0) begin
masking_cg.sample(0, 0, masking_result);
return;
end
if (x == {WIDTH{1'b1}}) begin
masking_cg.sample({WIDTH{1'b1}}, WIDTH + 1, masking_result);
return;
end
for(int i=0;i<WIDTH;i++)begin
masking_cg.sample(x, (i+1), masking_result);
end
endfunction
```

## Utility Class for Bitwise Coverage

I've created a short example on how to use the above bitwise coverage methods. You can view and download the example's source code from AMIQ's GitHub.

All that's left now is to decide which coverage pattern should become part of your project's verification goals.

PS: In addition, I also recommend you read this Bithacks article containing a comprehensive bitwise operation list, as compiled by Sean Eron Anderson.

## Madhusudan Eyunni April 12th, 2016 18:40:26