Coverage Cookbook/Coding for analysis/ru
Материал из Wiki
- Метрики и процессы покрытия (en)
- Метрики покрытия кода (en)
- Метрики функционального покрытия (en)
- Specification to testplan (en)
- Testplan to functional coverage (en)
- Coding for analysis (en)
- Coverage Examples (Practice) (en)
- Requirements Writing Guidelines (en)
Taking care with the implementation of covergroups is an investment in time that can pay back when you or someone else need to understand where the missing functional coverage is.
Содержание |
Covergroup Labeling
The way in which you use labeling when coding a covergroup can have a huge impact on understanding the coverage results. A covergroup can be assigned a option.name string which helps with identification of which particular part of a testbench the coverage is associated with. In side a covergroup, coverpoints can be labelled and bins can be named. Using all of these techniques makes it much easier to understand the coverage results during analysis.
Covergroup naming
If multiple instances of the same covergroup are used within a testbench, then the option.name parameter can be used to assign an identity string to each instance. The name string can be passed in as an argument when the covergroup is constructed. In a UVM environment, the name could be passed in using get_full_name() method.See the following code example.
// Class containing a covergroup class my_cov_mon; covergroup my_cg(string instance_name); option.per_instance = 1; option.name = instance_name; // ... endgroup: my_cg function new(string cg_inst_nmae); my_cg = new(cg_inst_name); // ... endfunction endclass: my_cov_mon // UVM Component containing a covergroup class my_cov_mon extends uvm_subscriber #(my_txn); covergroup my_cg(string instance_name); option.per_instance = 1; option.name = instance_name; // ... endgroup: my_cg function new(string name = "my_cov_mon", uvm_component parent = null); super.new(name, parent); my_cg = new(this.get_full_name()); // Gets the UVM hierarchy for the component endfunction endclass: my_cov_mon
A covergroup can also be named programatically using the covergroup set_inst_name() built-in method.
// UVM Covergroup based component class my_cov_mon extends uvm_subscriber #(my_txn); covergroup my_cg; //... endgroup: my_cg function new(string name = "my_cov_mon", uvm_component parent = null); super.new(name, parent); my_cg = new(); endfunction function void build_phase(uvm_phase phase); my_cg.set_inst_name("TLB_coverage"); // Sets the instance name //... endfunction: build_phase endclass: my_cov_mon
Coverpoint and bin labeling
As an example consider the following functional coverage problem from the UART example. In this example the UART word format is determined by the contents of the Line Control Register (LCR):
LCR Bit | Value | Description |
---|---|---|
[1:0] | 2'b00 | 5 bit data character |
2'b01 | 6 bit data character | |
2'b10 | 7 bit data character | |
2'b11 | 8 bit data character | |
2 | 1'b0 | 1 Stop bit |
1'b1 | 2 Stop bits | |
[5:3] | 3'b??0 | No Parity |
3'b001 | Odd parity | |
3'b011 | Even parity | |
3'b101 | Stick 0 parity | |
3'b111 | Stick 1 parity |
Lower Analysis Potential
covergroup tx_word_format_cg() with function sample(bit[5:0] lcr); option.name = "tx_word_format"; option.per_instance = 1; coverpoint LCR[5:0]; endgroup: tx_word_format_cg |
Higher Analysis Potential Arrow.png
covergroup tx_word_format_cg with function sample(bit[5:0] lcr); option.name = "tx_word_format"; option.per_instance = 1; WORD_LENGTH: coverpoint lcr[1:0] { bins bits_5 = {0}; bins bits_6 = {1}; bins bits_7 = {2}; bins bits_8 = {3}; } STOP_BITS: coverpoint lcr[2] { bins stop_1 = {0}; bins stop_2 = {1}; } PARITY: coverpoint lcr[5:3] { bins no_parity = {3'b000, 3'b010, 3'b100, 3'b110}; bins even_parity = {3'b011}; bins odd_parity = {3'b001}; bins stick1_parity = {3'b101}; bins stick0_parity = {3'b111}; } WORD_FORMAT: cross WORD_LENGTH, STOP_BITS, PARITY; endgroup: tx_word_format_cg |
In order to check that all possible word formats have been transmitted we could implement a covergroup by creating a coverpoint for LCR[5:0] and not specifying any bins. This would create a set of default bins, one for each possible value of the register, as shown in the left hand code example. If the functional coverage collected samples these bits at least once, then there is no problem, but if not then it is reasonably difficult to figure out which bin corresponds to which condition - see the 'before' screen shot from the Questa covergroup browser. Here, not using labels has caused the simulator to use auto-bins, which means that the missing bin values need to be converted to binary and then mapped to the register fields to identify the missing configurations.
A better way to implement the covergroup is to use a labeled coverpoint for each register field and then using the bins syntax for each of the values in the register truth table. When this is simulated, the cross products created reflect the different bin labels, which makes it much easier to determine which functional coverage conditions have not been sampled. It also makes it easier to see whether there are any gross coverage conditions that have been missed. See the 'after' screen shot from the Questa covergroup GUI for the refactored covergroup.
How Covergroup options affect the reporting and computation of coverage
Implementation Options
The analysis of functional coverage information is affected by the way in which the coverage results are reported. There are three covergroup options which impact coverage reporting and can cause considerable confusion, and these are:
- option.per_instance
- option.get_inst_coverage
- type_option.merge_instances
If these options are not specified in the code that implements a covergroup, then they are not enabled by default. In other words, they are set to 0.
These three options should be explicitly declared in covergroup to ensure that the coverage computation and reporting is consistent and as required.
Covergroup types and instances
When a covergroup is declared it becomes a type that may be instantiated several times in the testbench - for instance the same type of interface is used for several ports in a design and therefore the same covergroup is used to measure protocol coverage. The default coverage reporting method is to report the coverage for the covergroup type as a weighted average of the coverage from all of the covergroups of that type. What this means is that if one of the ports has been exercised to achieve 100% coverage, but others have not, then the coverage reported will not be less than 100% and it will not be possible to analyse which interfaces have not been exercised.
per_instance option
If the covergroup option.per_instance is set to 1, then the covergroup reporting is broken out per instance, but the overall coverage reported is still the weighted average. In the example quoted, this would enable the coverage for each port to be examined, possibly leading to a detection of a design bug or a short-coming in the stimulus generation.
merge_instances option
If the covergroup type_option.merge_instances is set to 1, then the overall coverage reported for all the instances of the covergroup is a merge, or logical OR, of all the coverage rather than a weighted average. This is potentially useful if you have multiple instances of the same design IP and and it is being exercised in different ways by different parts of the testbench. One outcome from using the merge_instances option is that one covergroup instance achieves 100% coverage masking another instance that achieves 0% coverage, since the overall coverage will be reported as 100%.
get_inst_coverage option
To help with the scenario where the merge_instances option has been enabled, the option.get_inst_coverage variable can be set to 1 to enable the SystemVerilog $get_inst_coverage() system call to return the coverage for an instance of a covergroup, therefore allowing the coverage for all individual instances to be checked. If the merge_instances option is set to 0, then the get_inst_coverage variable has no effect.
Summary
Interaction between per_instance and merge_instances settings:
option.per_instance | type_option.merge_instances | Coverage reporting behaviour |
---|---|---|
0 | 0 | Overall coverage reported as a weighted average of the coverage for all instances of the covergroup |
1 | 0 | Overall coverage reported as a weighted average of the coverage for all instances of the covergroup, and broken out for each instance of the covergroup. |
0 | 1 | Overall coverage reported as a merge of the coverage for the individual instances of the covergroup |
1 | 1 | Overall coverage reported as a merge of individual coverage results, get_inst_coverage() enabled, coverage reporting broken out for each instance of the covergroup |
Interaction between merge_instances and get_inst_coverage
type_option.merge_instances | option.get_inst_coverage | Data returned by $get_inst_coverage() |
---|---|---|
1 | 0 | Type based coverage - i.e. merge of all instance coverage |
1 | 1 | Instance specific coverage |
0 | 1|0 | Instance specific coverage |
Simulator Specific Run Time Options
The options described are covergroup options defined in the SystemVerilog LRM and can therefore be added to covergroup code. Simulators also add command line options which allow users to change the way in which coverage is reported, although these options tend to be global affecting all covergroups within the testbench being simulated.