1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | class my_item; rand bit [7:0] data_size; rand bit [7:0] inside_data_size; constraint my_c { inside_data_size <= data_size; inside_data_size >= 0; } endclass //-----------------------------------------// class my_cov; my_item itm_p; //---------// covergroup my_grp; data_size_cp: coverpoint itm_p.data_size; inside_data_size_cp: coverpoint itm_p.inside_data_size { bins zero = {0}; bins lower_half = {[1:(itm_p.data_size/2)-1]}; bins half = {itm_p.data_size/2}; bins upper_half = {[(itm_p.data_size/2)+1:itm_p.data_size-1]}; bins max = {itm_p.data_size}; } endgroup //----------// function new(); my_grp = new; endfunction: new //----------// task do_sample(); my_grp.sample(); endtask: do_sample endclass //-----------------------------------------// module tb(); initial begin my_item itm_p; my_cov cov_p; cov_p = new(); itm_p = new(); for(int i = 0; i < 10; i++) begin itm_p.randomize(); cov_p.itm_p = itm_p; cov_p.do_sample(); end end endmodule: tb |
Needless to say, this coverpoint implementation would not work. If you don't see the problem here, I will point it out; When the covergroup is created, there is an attempt to access the object member "itm_p.data_size" for the coverbin "inside_data_size_cp". Since an object has not been created yet and the current object pointer is null, a null pointer access is triggered terminating the simulation. So such an implementation is obviously not the way to go however the dependency for our coverage model still exists. Lets go for a different coverbin implementation, one that will work.
What is needed is a variable which is a function of the data size but not a member of the sampled object. This new variable can be a member is the coverage class wrapper and updated right before the covergroup sampling.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | class my_cov; typedef enum { ZERO = 0, LOWER_HALF, HALF, UPPER_HALF, MAX } e_inside_data_size; my_item itm_p; e_inside_data_size inside_data_size_e; //---------// covergroup my_grp; data_size_cp: coverpoint itm_p.data_size; inside_data_size_cp: coverpoint inside_data_size_e; endgroup //----------// function new(); my_grp = new; endfunction: new //----------// task do_sample(); case(1) (itm_p.inside_data_size == 0): inside_data_size_e = ZERO; (itm_p.inside_data_size > 0 && itm_p.inside_data_size < itm_p.data_size/2): inside_data_size_e = LOWER_HALF; (itm_p.inside_data_size == itm_p.data_size/2): inside_data_size_e = HALF; (itm_p.inside_data_size > itm_p.data_size/2 && itm_p.inside_data_size < itm_p.data_size ): inside_data_size_e = UPPER_HALF; (itm_p.inside_data_size == itm_p.data_size): inside_data_size_e = MAX; default: $error("inside_data_size=%x is not within data_size of %x", itm_p.inside_data_size, itm_p.data_size); endcase my_grp.sample(); endtask: do_sample endclass |
In the last example an enum type was created defining different value ranges for our coverbin. A class member is defined in the coverage wrapper class using this enum as the type. Right before the covergroup is sampled, this member is updated as a function of the received item using the case(1) statement. So now the coverbin no longer creates a null pointer access and the required dependency for the functional coverage is fulfilled.