ロジック検証でのカバレッジ駆動開発(CDD)

---
## 1. はじめに

従来の**半導体のロジック検証**では、「テストベンチを書いて、波形を眺めて、問題なければOK」という流れが多く見受けられました。しかし設計が大規模化するほど、**「本当に全部のパターンをチェックできているのか？」** が見えにくくなります。

そこで、[前回の記事](https://qiita.com/sukimaengineer/items/064bff52502edfeee1bf) では、**SVA (SystemVerilog Assertions) の基本**を解説しました。  
SVA を使った**機能的カバレッジ (cover property)** も有効ですが、今回の主眼は、**ステートメント/ブランチ/トグル/FSM カバレッジ** などの “構造的カバレッジ” に焦点を当てます。さらに、カバレッジ解析でしばしばレポートされる**到達不能コード (UNR)** や**undetermined** への対処も解説します。  

これによって、**「RTL コードをどこまでテストで通せているか」** を数値化し、さらなる検証補強を図ることが可能となります。  

- ※実際の大規模プロジェクトでは、**機能的カバレッジと構造的カバレッジ**を組み合わせ、複数のテストログを**マージ**（合算）しながら到達度を上げる手法が一般的です。本記事では特に「構造的カバレッジ」側にフォーカスして解説します。

### 用語メモについて
この記事では、**CDD (Coverage-Driven Development)** や **Exclude**, **Disabled** など、いくつかの専門用語を用います。文中で初登場する際に簡単に説明していますが、詳細は記事末尾の[「用語集」](#用語集)にもまとめましたので、必要に応じて参照してください。

---

## 2. 構造的カバレッジ vs. 機能的カバレッジ

### 2.1 構造的カバレッジとは

- **ステートメント / ブランチ / トグル / FSM** など、RTL コードの**どの部分を実際にテストで通過したか**を測定する  
- 実行パスが通っていなかったり、信号のビットが切り替わっていなかったりすると Uncovered と判定される  
- **UNR (Unreachable)** や **undetermined** がレポートに含まれた場合、その真偽や原因を精査する必要がある

<details>
<summary>簡単なRTL例: ステートメント/ブランチ/トグル</summary>

```systemverilog
module example(
    input  logic clk,
    input  logic reset_n,
    input  logic [3:0] in_data,
    output logic out_flag
);

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        out_flag <= 1'b0;    // [A] ステートメント
    end else begin
        // [B] branch条件(if-else)の True/False 両パターン踏めるか?
        if (in_data == 4'hF) begin
            out_flag <= 1'b1;  // [C] ステートメント
        end else begin
            out_flag <= 1'b0;  // [D] ステートメント
        end
    end
end

endmodule
```

- **ステートメントカバレッジ** : `[A]`, `[C]`, `[D]` の行が一度でも実行されたか  
- **ブランチカバレッジ** : `if (in_data == 4'hF)` が True/False 両方通過されたか  
- **トグルカバレッジ** : `out_flag`(1ビット) が `0→1`, `1→0` の両方を経験したか  
- **UNR (例)** : `if (1'b0)` など、物理的に絶対に通らない分岐があるとツールが UNR と報告する  
</details>

### 2.2 機能的カバレッジとの違い

以下の図は、構造的カバレッジと機能的カバレッジの対比を示します。  

```mermaid
flowchart LR
    classDef scStyle fill:#E0F7FA,stroke:#00838F,color:#333,stroke-width:2px,stroke-dasharray: 5 5
    classDef fcStyle fill:#F3E5F5,stroke:#7B1FA2,color:#333,stroke-width:2px

    A[[構造的カバレッジ]]:::fcStyle --> B["ステートメント<br>ブランチ<br>トグル,FSM"] --> C["RTLコード<br>到達度を評価"]
    D[[機能的カバレッジ]]:::fcStyle --> E["covergroup<br>cover property"] --> F["仕様シナリオや<br>時相要件を評価"]
```

- **機能的カバレッジ** : 「仕様上、どのシナリオ・組み合わせ・時相が起きたか」をチェック  
  - 例: `covergroup` でレジスタ値の組み合わせを網羅  
  - 例: SVA の `cover property` で時相的シナリオ (割り込み→何サイクル後→ACK) を検証  
  - ⇒ **ユーザーが仕様に基づいて作り込む**ことが多い  
- **構造的カバレッジ** : 「RTL コードのどこ（行・分岐・ビット）を踏んだか」をチェック  
  - ⇒ ツールが RTL コードを解析して**自動的にポイントを生成**する  
- **両者の併用が理想** :
  - 構造的カバレッジだけだと「コードは通しているが、正しい組み合わせか不明」  
  - 機能的カバレッジだけだと「一部コードが未到達でも気づかない」  
  - 結果的に両面を見ると**より高品質な検証**が実現可能

---

## 3. Covered / Uncovered / UNR / undetermined とは？

カバレッジツールの解析結果では、**Covered / Uncovered / UNR (Unreachable) / undetermined** などが割り当てられます。  
| 項目                         | 意味                                         | テスト状況                 | 注意点<br>アクション                          |
|------------|-----------------|-----------|---------------------|
| Covered<br>実際に到達         | テストで実際に通した  | テストは行われており網羅   | バグの完全排除<br>を保証するもの<br>ではない |
| Uncovered<br>到達可能だが未到達 | ロジック上到達可能だが<br>テストされていない      | テスト不足                 | 追加テストが必要                            |
| UNR<br>Unreachable<br>論理的に到達不可能 | ツールが論理的に<br>絶対通らないと判定           | 本当に不要なら除外         | 誤判定の場合は<br>ツール設定や<br>テストを修正する   |
| undetermined<br>保留/情報不足 | ツールが<br>　Covered<br>　Uncovered<br>　UNR<br>いずれにも<br>決められず保留 | 分類未確定                 | 原因を調査<br>（ログ不足・設定不備<br>などを確認）   |

```mermaid
flowchart LR
    classDef coveredStyle fill:#C8E6C9,stroke:#2E7D32,color:#333,stroke-width:2px
    classDef uncoveredStyle fill:#FFCDD2,stroke:#C62828,color:#333,stroke-width:2px
    classDef unrStyle fill:#FFE082,stroke:#F57F17,color:#333,stroke-width:2px
    classDef undetStyle fill:#CFD8DC,stroke:#455A64,color:#333,stroke-width:2px

    A["Covered:<br>実際に到達"]:::coveredStyle
    B["Uncovered:<br>到達可能だが未到達"]:::uncoveredStyle
    C["UNR (Unreachable):<br>論理的に到達不可能"]:::unrStyle
    D["undetermined:<br>情報不足などにより保留"]:::undetStyle

    A -->|"テストで通った"| Ainfo(バグの完全排除を<br>保証するものではない)
    B -->|"テスト不足"| Binfo(追加テストが必要)
    C -->|"本当に不要なら除外"| Cinfo(誤判定なら修正)
    D -->|"要確定<br>Covered ?<br>Uncovered ?<br>UNR ?"| Dinfo(原因を調査)
```

- **Covered** : テストで実際に通した  
- **Uncovered** : 到達可能だがテスト不足 (追加テストが必要)  
- **UNR (Unreachable)** : 物理的/論理的に「絶対に通らない」とツールが判定  
- **undetermined** : ツールが分類できずに保留 (解析情報不足や設定不備が原因)

---

## 4. ステートメント/ブランチ/トグル/FSM と UNR, undetermined について

ここでは主な指標と、UNR・undetermined との絡みを簡単に再確認します。

### 4.1 ステートメントカバレッジ
- RTL上の各行 or 文単位が実行されたか  
- 例: `if (!reset_n)` 節や `else` 節などが一度でも入れば covered  

### 4.2 ブランチカバレッジ
- `if/else, case` 等の True/False/各分岐先が踏まれたか  
- マクロで実質消えている分岐があると UNR 報告されやすい  

### 4.3 トグルカバレッジ
- 信号ビットが 0→1 / 1→0 と変化したか  
- **ゲートレベルのトグルカバレッジ** もあるが、本記事では主に RTL レベルを想定

### 4.4 FSMカバレッジ
- FSM の各状態や遷移を網羅しているか  
- 大規模 FSM では複数テストのログを**マージ**しないと網羅が難しい

### 4.5 UNR (到達不能コード)
- ツールが「絶対に通らない」と断定した箇所  
- マクロ OFF などで常にスキップされる分岐がある場合、UNR として報告  
- **ツール誤判定**の可能性もあるため、設計者と相談が必要

### 4.6 undetermined
- ツールが「Covered/Uncovered/UNR」と判断できず、保留になった箇所  
- 原因: ログマージミス、部分ログ、設定ファイルの不整合、階層名の相違 など  
- 解消法: ログファイルのパスを正す、フルログを取得、設定ファイルを再確認、など

---

### 4.1.1 簡単なRTL例: ステートメント/ブランチ/トグル (図＆表で解説)

以下では、**具体的な RTL コード例**を示し、ステートメントカバレッジ・ブランチカバレッジ・トグルカバレッジをどのように確認するかを図や表で解説します。

#### 1. RTLコード例

```systemverilog
module example(
    input  logic clk,
    input  logic reset_n,
    input  logic [3:0] in_data,
    output logic out_flag
);

always_ff @(posedge clk or negedge reset_n) begin
    if (!reset_n) begin
        out_flag <= 1'b0;    // [A] ステートメント
    end else begin
        // [B] ブランチ条件（if-else）の True/False 両方踏めるか？
        if (in_data == 4'hF) begin
            out_flag <= 1'b1;  // [C] ステートメント
        end else begin
            out_flag <= 1'b0;  // [D] ステートメント
        end
    end
end

endmodule
```

- `[A]`, `[C]`, `[D]` は「ステートメントカバレッジ」の対象箇所。  
- `[B]` は「ブランチカバレッジ」の対象となる `if(in_data == 4'hF)` の分岐条件。  
- `out_flag` という1ビット信号が「トグルカバレッジ」の対象（0→1 / 1→0 の両方を踏むかどうか）になります。

#### 2. 簡単なブロック図

下図は、上記の RTL コードの主要ポイントを簡単なブロック図風に示したものです。  
あくまでイメージ図であり、回路合成を厳密に表すわけではありません。

```mermaid
flowchart LR
 subgraph statement/branch/toggle
  direction TB
    %% 四角形[ ] ノード: A, A2
    A["in_data[3:0]"] --> B{"if (in_data==4'hF) ?"} 
    B -- "True" --> C[["out_flag <= 1'b1"]]
    B -- "False" --> D[["out_flag <= 1'b0"]]
    A2["reset_n"] -- "if (!reset_n) ?" --> Z[["out_flag <= 1'b0"]]
    C --> E[["out_flag<br>toggle対象"]]
    D --> E
    Z --> E
 end
```

- **[A]:** `if(!reset_n)` で `out_flag <= 1'b0;` を実行 (ステートメントカバレッジ)  
- **[B]:** `if(in_data == 4'hF)` のブランチ条件 (ブランチカバレッジ)  
- **[C], [D]:** それぞれの分岐先で代入する箇所 (ステートメントカバレッジ)  
- **out_flag:** 0 と 1 の両方に切り替わるなら「トグルカバレッジ」達成  

#### 3. カバレッジポイントの表

以下に、カバレッジの種類・対象箇所・Coveredになる条件などをまとめた表を示します。

| カバレッジ種類              | 対象コード/信号           | Covered になる条件例                                                                  | Uncovered になる例                                                |
|----------|-----------|--------------|------------|
| **ステートメント**          | `[A]` <br>`out_flag <= 1'b0;` (if reset) <br> `[C]`, `[D]` 各行 | - `reset_n`が 0→1 に切り替わる際に `[A]` が実行される <br> - `in_data == 4'hF` で `[C]` <br> - それ以外で `[D]`                           | - テストで一度も reset をアサートしない <br> - `in_data` が常に 4'hF 以外 (あるいは常に4'hF) など |
| **ブランチ**                | `[B]` <br>`if (in_data == 4'hF)` | - `in_data` を 4'hF にして True 分岐を踏む <br> - 4'hF 以外にして False 分岐を踏む     | - True か False のどちらか一方のみ踏んで終わる <br> - in_data が常に一定値 |
| **トグル**                  | `out_flag` (1ビット)     | - シミュレーション中に `out_flag` が 0→1、1→0 を両方経験する                          | - 常に 0 のまま or 常に 1 のまま                                   |

##### ポイント
- **ステートメントカバレッジ** は各行が1度でも実行されれば Covered。  
- **ブランチカバレッジ** は True と False 両パターンを実行する必要がある。  
- **トグルカバレッジ** はビットが片側に固定されていないかをチェックする指標。

#### 4. まとめ

- 具体的な RTL コード例と図・表を使うと、どの行や分岐・信号がどのカバレッジに対応しているかが一目瞭然になります。  
- テストベンチでは、リセットのタイミングや `in_data` の設定バリエーションを用意し、ブランチ (if-else) の両方を通したり、`out_flag` を 0→1→0 と切り替えるなどを意識すると、各カバレッジを効率よく上げることが可能です。  

---

## 5. CDD(Coverage-Driven Development) において把握すべき他の特別カテゴリ

### 5.1 Excluded / Ignored
- ユーザーが「本当に不要」と判断し、**カバレッジ計算から外す**  
- ツール自動判定の UNR とは別に、**手動で除外**する場合もある  
- むやみに Exclude しすぎると検証不足リスク  
- **主要ツール例**  
  - Cadence IMC: `exclude_spec`  
  - Synopsys VCS/Verdi: GUI あるいはコマンドで Ignore/Exclude  
  - Siemens (Mentor) Questa: GUI / コマンド両方で設定  
  - 用語や操作がツールごとに異なる

### 5.2 Disabled
- **特定カバレッジ種別/階層を、最初から測定しない**(例: トグルを無効)  
- レポート上 “disabled” と表示されるか、そもそも表示されない  
- **実践的使い分け**  
  - 解析時間を減らすため、トグルだけ一時的に無効化する  
  - 後からオンにした場合、過去ログにはトグル情報が無いため再取得が必要

---

## 6. 構造的カバレッジを向上させるフロー (UNR & undetermined を正しく扱う)

下記フローは、UNR/undetermined を正しく処理し、構造的カバレッジを高水準へ導く手順です。

```mermaid
flowchart TB
    classDef stepStyle fill:#FFF9C4,stroke:#FBC02D,stroke-width:2px,color:#333

    A[[カバレッジレポート<br>に UNR/undetermined ?]]:::stepStyle --> B{UNR or<br> undetermined ?}:::stepStyle
    B -->|UNR| C{論理的に<br>本当に行けない ?}:::stepStyle
    B -->|undetermined| D[原因を調査:<br>ログ不備 / 部分ログ /<br>設定不整合など]:::stepStyle
    C -->|Yes| E[不要なら<br>Exclude]:::stepStyle
    C -->|No| F[テスト不足 or<br>ツール誤判定<br>↓<br>Directed追加 ? 設定修正 ?]:::stepStyle
    D --> G[Covered<br>Uncovered<br>UNR<br>に最終確定]:::stepStyle
    E --> H[再マージ<br>↓<br>レポート]:::stepStyle
    F --> H
    G --> H
    H --> I[[構造的カバレッジ向上]]:::stepStyle
```

- **UNR** → 本当に行けないか、ツール誤判定か  
- **undetermined** → ログ不備や設定ミスがないか  
- **テスト追加** → uncovered を covered に変える  
- **Exclude** → 設計者合意のもと不要部分を除外  

---

## 7. 構造的カバレッジ 100% でも完璧ではない

構造的カバレッジが 100% であっても、すべての不具合を検出できるとは限りません。  
「行や分岐を**一度通過した**」だけで、実際に期待どおりの動きをしたかどうかは別問題です。

```mermaid
flowchart LR
    classDef warnStyle fill:#FFE0B2,stroke:#FB8C00,color:#333,stroke-width:2px

    X[構造的カバレッジ <br>100%達成]:::warnStyle --> Y["まだバグはない？"]:::warnStyle
    Y -->|必ずしも<br>そうではない| Z[機能的 or<br>タイミング的バグ<br>の見落とし]:::warnStyle
```

### 例：構造的カバレッジだけでは見落とす可能性のあるケース

- **if-else 文の else 節は通ったが、内部の重要処理が特定パラメータの場合だけスキップされる**  
  - 一度でも `else` に入ればステートメントカバレッジは Covered だが、微妙な条件下での動作を網羅していない  
- **同じ分岐を通っていても、時系列上の順番が異なる場合に不具合が起きる**  
  - 例: 同じ `else` パスでも、クロック間のタイミングが重なるとレジスタが正常に更新されない  
- **大きい配列や複雑な FSM で、一部の組み合わせは踏んでいない**  
  - ステートメントやブランチは通っているが、実は内部ループが十分に回せていない

#### 結論
- 構造的カバレッジ = **テスト網羅度**の指標にすぎません  
- **機能的カバレッジ** (cover property) や **SVA** (アサーション) と組み合わせることで、より正確な検証が行われることが推奨されます

---

## 8. SVA は構造的カバレッジを直接計測しないが、効果的な補強となる

**SystemVerilog Assertions (SVA)** は、**機能検証**（アサーション / cover property）を主目的とする仕組みです。  
ステートメントやブランチなどの**構造的カバレッジを直接計測**するわけではありませんが、**検証プロセス全体を通して構造的カバレッジ向上に貢献**します。

1. **アサーション達成のためにテスト強化**  
   - 未到達分岐を含む複雑なシナリオを組むことで結果的に covered が増える  
2. **制約ランダム + アサーション**  
   - 広範囲の入力を発生させる → 思わぬ箇所の分岐や状態を踏む  
3. **カバレッジガイド**  
   - `cover property` で特定状態遷移や条件を追いかけると、それに必要なテストが追加され、構造的カバレッジも高まる

> **UVM** (Universal Verification Methodology) を用いると、制約ランダムテストと SVA を組み合わせ、**複数のテスト**をマージしながら継続的にカバレッジを監視するフローが確立しやすいです。  

---

## 9. FAQ

### **Q1. “Covered” と表示されても機能的には万全ではない？**

A. Covered は**「最低一度は通せた」**だけで、機能的/時相的な正しさは別です。  
- **SVA** や**機能的カバレッジ**を組み合わせてこそ本当の安心感  
- 構造的カバレッジ = コード到達度、と割り切る

---

### **Q2. “Uncovered” と UNR の違いは？**

A. **Uncovered** → 到達可能なのにテスト不足  
**UNR (Unreachable)** → 論理的に絶対行けない  
- Uncovered は追加テストで covered 化が可能  
- UNR は不要なら除外、実際には通るならツール設定 or テスト修正

---

### **Q3. undetermined が再マージで “UNR” に変わったのは？**

A. 新たなテストや設定により「やはり到達不能」と確定したのでしょう。  
- 逆に covered/uncovered に変わるパターンも  
- **undetermined = 保留状態** なので、ログ/設定が整うと最終ステータスに決まります

---

### **Q4. Exclusion してもカバレッジ率が上がりません。**

A. 除外箇所が少ない or 全体母数が大きい場合、率変化が小さいです。  
- 大規模 FSM を除外すると大幅に変わるかも  
- 必要箇所を誤って除外しないように注意

---

### **Q5. “Disabled” な箇所を再度測定対象にしたい？**

A. ツール設定で解析をオンにし、改めてシミュレーション＆カバレッジ取得を行います。  
- 過去ログにはデータがないので、新規テストが必要

---

### **Q6. Dead code と Unreachable code は同じですか？**

A. 部分的には重なる場合がありますが、まったく同じではありません。  
- **Unreachable code** : 矛盾条件等で行けない  
- **Dead code** : 今は呼ばれていないだけで、将来使うなど広い概念  
- 半導体ロジック検証では **Unreachable code** の方が焦点になりやすい

---

### **Q7. アサーション(SVA)で構造的カバレッジを上げられますか？**

A. **SVA** は**機能検証**向けで、構造的カバレッジを**“直接”**上げる機能はありません。  
- ただし、アサーション達成のためのテスト拡張で未到達箇所を通すなど、**間接的**に上げる可能性は大いにあります

---

### **Q8. Uncovered が減ったら undetermined が 0 になる事はありますか？**

A. 解析やテスト追加、設定修正が進むと起こり得ます。  
- **uncovered が大幅に covered 化**したり、不要箇所を除外(Exclude)して減る  
- **undetermined** は“暫定保留”状態でしたが、最終的に Covered/Uncovered/UNR のいずれかに確定して 0 になる  
- ログや設定が整ってマージがうまくいくとこうなります。  
  - **undetermined = 0** は「曖昧な部分がない」好ましい状態です。
---

## 10. まとめ

**カバレッジ駆動開発 (CDD)** では、**構造的カバレッジ**(ステートメント/ブランチ/トグル/FSM) を駆使し、テスト不足を洗い出して**検証漏れ**を最小化することが重要です。  
- **Covered / Uncovered / UNR / undetermined** を正しく扱い、必要に応じて **Exclude** や **Disabled** を設定する  
- **複数テストログのマージ** を活用して網羅度を高める  
- **構造的カバレッジ 100%** でも、機能面やタイミング面での不備は残る可能性があるため、**機能的カバレッジ**(SVA, covergroup) と併用する

これらを実施することで、ロジック検証の品質をさらに高められます。

:::note info
- 構造的カバレッジは「テスト網羅度」を可視化する指標であり、バグの完全排除を保証するものではありません。  
- 大規模検証では**複数テストログをマージ**し、ツール設定やログファイルの整合性をチェックすることで、正確なカバレッジを算出することが推奨されます。  
- 本記事で紹介した**UNR, undetermined, Excluded, Disabled** などのステータスや分類はツールによって用語や扱いが異なる場合があります。公式ドキュメントを参照してください。  
- **SVA** は構造的カバレッジを直接計測しませんが、検証プロセスの質を高め、結果的にカバレッジ向上にも寄与する有力な手段です。
:::

---

<a id="用語集"></a>
## 用語集

- **RTL (Register-Transfer Level)**  
  クロック単位の回路動作を記述する設計レイヤー。SystemVerilog や VHDL で書かれることが多い。  

- **SVA (SystemVerilog Assertions)**  
  SystemVerilog におけるアサーション機能。機能的検証やカバレッジ (cover property) を定義できる。  

- **UVM (Universal Verification Methodology)**  
  SystemVerilog クラスベースの検証フレームワーク。制約ランダムテストとアサーションを組み合わせ、カバレッジを効率的に上げる手段として使われる。

- **CDDV (Coverage-Driven Development)**  
  カバレッジを指標としてテストを拡充し、網羅度を高めながら検証を進める手法。大規模化した設計での漏れを減らす効果がある。  

- **Exclude / Ignored**  
  カバレッジの計算対象から外す行為 (ツールの機能)。本当に不要な箇所を除外する際に用いるが、乱用は検証漏れに繋がる。  

- **Disabled**  
  特定のカバレッジ種別 (例: トグル) や階層を最初から測定しない設定。解析負荷軽減や必要のない箇所を省くために使われる。

- **UNR (Unreachable)**  
  ツールが「論理的に通らない」と判定したコードや分岐。  
  実際に不要なら Exclude、必要ならテストや設定を修正して到達させる。

- **undetermined**  
  ツールが Covered / Uncovered / UNR いずれにも分類できず保留となったステータス。ログマージミスや設定不備が多い原因。

---

## 参考文献 / リンク

- [SystemVerilog アサーション (SVA) 入門](https://qiita.com/sukimaengineer/items/064bff52502edfeee1bf)  
  - **初学者向けに SVA の基本文法を解説**  
- [SystemVerilog for Verification (Chris Spear, Greg Tumbush)](https://www.springer.com/gp/book/9783319542860)  
  - **SystemVerilog の検証機能（クラスベース検証、UVM等）を網羅的に解説**する定評ある書籍 (英語) 

---

以上