Languages

CommunityCategory: XMODELMeasuring the coverage of a finite-state machine (FSM) in SystemVerilog

XMODEL

Measuring the coverage of a finite-state machine (FSM) in SystemVerilog

SA Support Team Staff 2022-12-29

I want to check if my simulation of a finite-state machine (FSM) is exercising all its states and all the possible transitions between the states. Can you show me an example of measuring such a coverage in SystemVerilog?

1 Answers
SA Support Team Staff 2022-12-29

Sure, I can show you an example of measuring the state and transition coverages of an FSM in SystemVerilog. As you know, measuring coverage metrics is an effective way to ensure that the simulation being performed on a device under test (DUT) is rich and thorough. That is, by measuring the state coverage, one can ensure that every state of the FSM has been visited during the simulation. Similarly, by measuring the transition coverage, one can ensure that every possible transition between the states has been traversed during the simulation.

Let's consider a simple FSM from this link for illustration. This FSM receives a 1-bit synchronous input L and produces a one-cycle-wide pulse for its 1-bit output P whenever the input L changes from 0 to 1. Shown below is the state transition diagram of this FSM. The FSM has a total of three states named LOW, RISE, and HIGH, and six possible transitions between them.

The following SystemVerilog module describes the above-mentioned FSM. It contains a set of flip-flops storing the current state of the FSM, a combinational logic determining the next state, and another combinational logic determining the output value. For ease of reading, the variables storing the current and next states are defined as enumerating types, which can take named values like LOW, RISE, and HIGH, instead of the 2-bit logic values like 2'b00, 2'b01, and 2'b10, respectively.

// MODULE fsm.sv
// An example finite-state machine asserting its output
// whenever its synchronous input changes from 0 to 1

module fsm (
    output out,
    input in,
    input clk
);
    typedef enum logic [1:0] {LOW=2'b00, RISE=2'b01, HIGH=2'b10} STATE;
    STATE cur_state, nxt_state;

    // combinational logic determining the next state
    always @(*) begin
        case (cur_state)
            LOW : nxt_state = (in) ? RISE : LOW;
            RISE : nxt_state = (in) ? HIGH : LOW;
            HIGH : nxt_state = (in) ? HIGH : LOW;
            default : nxt_state = (in) ? HIGH : LOW;
        endcase
    end

    // combinational logic determining the output value
    assign out = (cur_state == RISE) ? 1'b1 : 1'b0;

    // flip-flops updating the current state
    always @(posedge clk) begin
        cur_state = nxt_state;
    end

endmodule

Now, here is a SystemVerilog module named 'meas_cov' that can measure the state and transition coverages of this FSM. The covergroup named 'cg' defined within the module checks whether the value of 'state' sampled at the positive edge of 'clk' covers the entire set of LOW, RISE, and HIGH and the two consecutive values of 'state' cover the entire set of LOW->RISE, LOW->LOW, RISE->HIGH, RISE->LOW, HIGH->HIGH, and HIGH->LOW transitions during the simulation. When the simulation ends, the final coverage value reported by the get_inst_coverage() function is displayed on the screen.

// MODULE meas_cov.sv
// A module measuring the FSM state coverage

module meas_cov (
    input logic [1:0] state,
    input clk
);
    typedef enum logic [1:0] {LOW=2'b00, RISE=2'b01, HIGH=2'b10} STATE;

    covergroup cg @(posedge clk);
        option.per_instance = 1;
        coverpoint state {
            bins states[] = {LOW, RISE, HIGH};
            bins transitions =
                (LOW  => RISE, LOW),
                (RISE => HIGH, LOW),
                (HIGH => HIGH, LOW);
        }
    endgroup

    cg cg_inst = new();
    real cov = $get_coverage();

    final begin
        $display("*** FSM Coverage (%m): %.2f%%\n", cg_inst.get_inst_coverage());
    end

endmodule

SystemVerilog offers a 'bind' construct that lets you attach this coverage measurement module to the FSM module without altering its codes. The following top-level testbench module named 'tb_fsm' shows an example. It not only instantiates the FSM module as DUT and supplies a periodic clock and randomized input to it, but also binds the 'meas_cov' module to the DUT, enabling the coverage measurement. That is, during the simulation, the 'meas_cov' module collects the state and transition coverages of the FSM.

// TESTBENCH tb_fsm.sv
// A testbench feeding random input values to a FSM under test

module tb_fsm ();
    reg in, clk;
    wire out;

    // DUT instantiation
    fsm DUT (.*);

    // stimuli generation
    initial clk = 0;
    always #(1ns) clk = ~clk;

    always @(posedge clk) begin
        in = $urandom_range(1, 0);
    end

    // binding coverage measuring module
    bind DUT meas_cov cv_inst (.state(cur_state), .clk(clk));

    initial begin
        $xmodel_dumpfile();
        $xmodel_dumpvars();
    end

endmodule

For brevity, this example reports only the percentage value of the total coverage when the simulation is finished. For instance, when you run a long enough simulation using the attached example, you'd get a message like this:

$ xmodel tb_fsm.sv fsm.sv meas_cov.sv --top tb_fsm --simtime 100ns

... omitted ...

*** FSM Coverage (tb_fsm.DUT.cv_inst): 100.00%

In fact, this example showcased only the basic features of SystemVerilog for measuring coverage. For those who are interested in more advanced features, we recommend this DVCon paper from Duolos:

Doug Smith and John Aynsley, "A Practical Look @ SystemVerilog Coverage – Tips, Tricks, and Gotchas," DVCon US, 2011.

Attachment: fsm_cov_20221226.tar.gz

XMODEL

SystemVerilog에서 유한상태 머신(finite-state machine; FSM)의 coverage를 측정하는 방법

SA Support Team Staff 2022-12-29

유한상태 머신(finite-state machine; FSM)에 대해서 내가 수행하고 있는 시뮬레이션이 FSM의 모든 state들과 그 state들간의 가능한 모든 이동경로들을 방문하고 있는지 체크하고 싶습니다. 이와 같은 coverage를 SystemVerilog 상에서 측정하는 예제를 보여줄 수 있나요?

1 Answers
SA Support Team Staff 2022-12-29

Sure, I can show you an example of measuring the state and transition coverages of an FSM in SystemVerilog. As you know, measuring coverage metrics is an effective way to ensure that the simulation being performed on a device under test (DUT) is rich and thorough. That is, by measuring the state coverage, one can ensure that every state of the FSM has been visited during the simulation. Similarly, by measuring the transition coverage, one can ensure that every possible transition between the states has been traversed during the simulation.

Let's consider a simple FSM from this link for illustration. This FSM receives a 1-bit synchronous input L and produces a one-cycle-wide pulse for its 1-bit output P whenever the input L changes from 0 to 1. Shown below is the state transition diagram of this FSM. The FSM has a total of three states named LOW, RISE, and HIGH, and six possible transitions between them.

The following SystemVerilog module describes the above-mentioned FSM. It contains a set of flip-flops storing the current state of the FSM, a combinational logic determining the next state, and another combinational logic determining the output value. For ease of reading, the variables storing the current and next states are defined as enumerating types, which can take named values like LOW, RISE, and HIGH, instead of the 2-bit logic values like 2'b00, 2'b01, and 2'b10, respectively.

// MODULE fsm.sv
// An example finite-state machine asserting its output
// whenever its synchronous input changes from 0 to 1

module fsm (
    output out,
    input in,
    input clk
);
    typedef enum logic [1:0] {LOW=2'b00, RISE=2'b01, HIGH=2'b10} STATE;
    STATE cur_state, nxt_state;

    // combinational logic determining the next state
    always @(*) begin
        case (cur_state)
            LOW : nxt_state = (in) ? RISE : LOW;
            RISE : nxt_state = (in) ? HIGH : LOW;
            HIGH : nxt_state = (in) ? HIGH : LOW;
            default : nxt_state = (in) ? HIGH : LOW;
        endcase
    end

    // combinational logic determining the output value
    assign out = (cur_state == RISE) ? 1'b1 : 1'b0;

    // flip-flops updating the current state
    always @(posedge clk) begin
        cur_state = nxt_state;
    end

endmodule

Now, here is a SystemVerilog module named 'meas_cov' that can measure the state and transition coverages of this FSM. The covergroup named 'cg' defined within the module checks whether the value of 'state' sampled at the positive edge of 'clk' covers the entire set of LOW, RISE, and HIGH and the two consecutive values of 'state' cover the entire set of LOW->RISE, LOW->LOW, RISE->HIGH, RISE->LOW, HIGH->HIGH, and HIGH->LOW transitions during the simulation. When the simulation ends, the final coverage value reported by the get_inst_coverage() function is displayed on the screen.

// MODULE meas_cov.sv
// A module measuring the FSM state coverage

module meas_cov (
    input logic [1:0] state,
    input clk
);
    typedef enum logic [1:0] {LOW=2'b00, RISE=2'b01, HIGH=2'b10} STATE;

    covergroup cg @(posedge clk);
        option.per_instance = 1;
        coverpoint state {
            bins states[] = {LOW, RISE, HIGH};
            bins transitions =
                (LOW  => RISE, LOW),
                (RISE => HIGH, LOW),
                (HIGH => HIGH, LOW);
        }
    endgroup

    cg cg_inst = new();
    real cov = $get_coverage();

    final begin
        $display("*** FSM Coverage (%m): %.2f%%\n", cg_inst.get_inst_coverage());
    end

endmodule

SystemVerilog offers a 'bind' construct that lets you attach this coverage measurement module to the FSM module without altering its codes. The following top-level testbench module named 'tb_fsm' shows an example. It not only instantiates the FSM module as DUT and supplies a periodic clock and randomized input to it, but also binds the 'meas_cov' module to the DUT, enabling the coverage measurement. That is, during the simulation, the 'meas_cov' module collects the state and transition coverages of the FSM.

// TESTBENCH tb_fsm.sv
// A testbench feeding random input values to a FSM under test

module tb_fsm ();
    reg in, clk;
    wire out;

    // DUT instantiation
    fsm DUT (.*);

    // stimuli generation
    initial clk = 0;
    always #(1ns) clk = ~clk;

    always @(posedge clk) begin
        in = $urandom_range(1, 0);
    end

    // binding coverage measuring module
    bind DUT meas_cov cv_inst (.state(cur_state), .clk(clk));

    initial begin
        $xmodel_dumpfile();
        $xmodel_dumpvars();
    end

endmodule

For brevity, this example reports only the percentage value of the total coverage when the simulation is finished. For instance, when you run a long enough simulation using the attached example, you'd get a message like this:

$ xmodel tb_fsm.sv fsm.sv meas_cov.sv --top tb_fsm --simtime 100ns

... omitted ...

*** FSM Coverage (tb_fsm.DUT.cv_inst): 100.00%

In fact, this example showcased only the basic features of SystemVerilog for measuring coverage. For those who are interested in more advanced features, we recommend this DVCon paper from Duolos:

Doug Smith and John Aynsley, "A Practical Look @ SystemVerilog Coverage – Tips, Tricks, and Gotchas," DVCon US, 2011.

Attachment: fsm_cov_20221226.tar.gz