How to Implement Flexible Coverage Definitions (Part 3)

In the final part of this 3-post series (Part 1, Part 2), I will show a way of covering enum transitions and conditionally ignoring transitions to and from certain enum values.

For example, in the case of a CPU’s instruction set, you want to make sure that all possible combinations of two consecutive instructions are executed. The most straightforward implementation is to define an enum with all the opcodes, and a transition bin for the opcode:

class instruction;
  typedef enum {
    ADD, SUB, MUL, DIV,     // Arithmetic
    AND, OR, XOR, NOT,      // Logic
    JMP, JZ, JNZ, JLT, JGT, // Jumps
    CALL, RET,              // Function calls
    LOAD, STORE,            // Memory access
    INT,                    // Interrupt
    NOP                     // Miscellaneous
  } opcode_t;

  rand opcode_t opcode;

  covergroup instruction_cg();
    option.per_instance = 1;

    opcode_transitions: coverpoint opcode {
      bins opcode_transitions[] = (
        ADD, SUB, MUL, DIV, AND, OR, XOR, NOT, JMP, JZ, JNZ, JLT, JGT, CALL, RET, LOAD, STORE, INT, NOP
        =>
        ADD, SUB, MUL, DIV, AND, OR, XOR, NOT, JMP, JZ, JNZ, JLT, JGT, CALL, RET, LOAD, STORE, INT, NOP
      );
    }
  endgroup
...
endclass

This code accomplishes what we wanted, but what happens if the requirements change? Let’s suppose that several CPU versions must be created. Some of them won’t support the hardware DIV instruction, while others should include the vector arithmetic instructions VADD, VSUB, VMUL, and VDIV.

The way we defined the transition coverage doesn’t allow us to easily implement these changes. In addition, when instructions are added or removed, we have to update the definitions for both the enum and the transition bin. This process can become tedious and error prone as the number of instructions increases.

One possible solution is to add an opcode named INVALID in the enum definition and ignore all transitions to and from it:

class instruction;
  typedef enum {
    INVALID = -1,
    ...
  } opcode_t;

  rand opcode_t opcode;

  covergroup instruction_cg();
    option.per_instance = 1;

    opcode_transitions: coverpoint opcode {
      bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);
      ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] => INVALID);
    }
  endgroup
  ...
endclass

To exclude the DIV instruction from the transition coverage, we can use the conditional operator:

covergroup instruction_cg(bit disable_div);
  option.per_instance = 1;

  opcode_transitions: coverpoint opcode {
    bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);

    ignore_bins ignore_div =
      (disable_div ? DIV : INVALID => [INVALID:$]),
      ([INVALID:$] => disable_div ? DIV : INVALID);

    ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] => INVALID);
  }
endgroup

If disable_div is equal to 1, all transitions to and from DIV are ignored. Otherwise, the ignore_div has the same definition as the ignore_invalid bin. The same technique can be used to conditionally exclude the vector instruction from the coverage.

You can find a complete example below:

class instruction;
  typedef enum {
    INVALID = -1,
    ADD, SUB, MUL, DIV,     // Arithmetic
    AND, OR, XOR, NOT,      // Logic
    JMP, JZ, JNZ, JLT, JGT, // Jumps
    CALL, RET,              // Function calls
    LOAD, STORE,            // Memory access
    INT,                    // Interrupt
    NOP,                    // Miscellaneous
    VADD, VSUB, VMUL, VDIV  // Vector arithmetic
  } opcode_t;

  rand opcode_t opcode;

  covergroup instruction_cg(bit disable_div, bit disable_vector);
    option.per_instance = 1;

    opcode_transitions: coverpoint opcode {
      bins opcode_transitions[] = ([INVALID:$] => [INVALID:$]);

      ignore_bins ignore_div =
        (disable_div ? DIV : INVALID => [INVALID:$]),
        ([INVALID:$] => disable_div ? DIV : INVALID);

      ignore_bins ignore_vector =
        ([(disable_vector ? VADD : INVALID) : (disable_vector ? VDIV : INVALID)] => [INVALID:$]),
        ([INVALID:$] => [(disable_vector ? VADD : INVALID) : (disable_vector ? VDIV : INVALID)]);

      ignore_bins ignore_invalid = (INVALID => [INVALID:$]), ([INVALID:$] => INVALID);
    }
  endgroup

  function new();
    instruction_cg = new(.disable_div(0), .disable_vector(1));
    instruction_cg.set_inst_name("instruction_cg");
  endfunction
endclass

module test;
  initial begin
    static instruction instr = new;

    repeat (10000) begin
      void'(instr.randomize() with { opcode != INVALID; });
      instr.instruction_cg.sample();
    end
  end
endmodule

Comments

3 Responses

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.

Subscribe to our newsletter

Do you want to be up to date with our latest articles?