0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

RTLはバイブコーディングでどこまで実装できるのか?(2026年6月編)

0
Posted at

前置き

世はバイブコーディング時代

最近はAIにコードを書かせるのが当たり前になってきた。
このあたりは「AIがかなり使える」という認識の人も多いと思う。

ただ、RTL設計だけは別世界だと思っていないだろうか。

「ソフトウェアは書けてもRTLは無理」
「検証まで含めると人間がやるしかない」

少なくとも1年前なら、その認識はかなり正しかったとおもう。
しかし2026年6月現在、状況はだいぶ変わってきている。

本当にRTLはAIの苦手分野のままなのか?
ステートマシンとカウンタを書かせてキャッキャするだけの玩具なのか?
そこらへんの肌感が伝わればいいなと思っています。

この記事は何?

AIにDMAコントローラの実装を依頼し、

  • 設計仕様書
  • 検証仕様書
  • Verilog RTL
  • テストベンチ
  • カバレッジ測定環境
  • 検証結果報告書

まで一式生成させてみる。

目的は「実務投入」ではない。

AIがどこまでRTL開発を理解しているのかを観察しながら、
生成された成果物を眺めてニヤニヤするための記事である。

結果

わかりやすく数字でまとめてみた。

モデル 実行時間 消費トークン数 コスト 設計 実装 検証 行カバレッジ
Opus4.8 1M (MAX) 90分 1432.8万 $15.64 (3,309円) 38/39 (97%)
GPT-5.5 1M (Extra High) 15分 342.1万 $4.01 (602円) 100/120 (83%)
GPT-5.4 Mini Medium 60分 1807.0万 $6.82 (1,023円) 92/97 (95%)
Composer2.5 20分 537.7万 $2.61 (392円) 115/123 (94%)

※円は1ドル150円換算
※時間についてはいろいろロスタイムがあったので、参考程度と思っていただければ。

設計

すごく当たり前のように聞こえるかもしれませんが「明記したところはその通りになっているが、曖昧な部分はイメージと違う」ということ。
DMAであれば、WriteポートとReadポートは別にしないと帯域がとれないので人が設計するなら分けますが、特に明記していないのでメモリポート1個で設計してきた。
インターフェースについても特に指定しませんでしたが、自由なプロトコルで実装されました。
コメントヘッダや命名規則など、skillsなどでしっかり指定したものはその通りにやってくれるしチェックもしてくれました。
ふわっとした部分は高価なモデルほどマシ。趣味なら札束で叩くのもいいと思います。でもそれってガチャなんですよね。
仕事なら毎回おなじ結果が出るようskillsrulesをしっかり整備してやるのが大事。
そうすればローカルLLMでも十分な成果が得られるように思います。

RTL実装

総じて微妙です。RTLじゃないんだよなぁというか。どうもソフトっぽい書き方になりますね…。
パッと見のコードとしてはそんなに悪くないんです。でも、やはりちょいちょいHDLとして許したくない記述がまだまだ出てきます。

厄介なのは、それなりに動いちゃうってことです。
微妙だと感じる記述は、ツールやシードによって解釈が変わったりバグを誘発しやすい記述なわけで、8割方問題ないけど残り2割で大炎上し徹夜する羽目になった、みたいなやつです。
こういったところを、いかにして.cursorのskillsrulesで押さえていくかが重要そう。

たとえばこういうコードです。

    // Status aggregation
    always @(posedge CLK) begin
        if (RESET) begin
            r_busy  <= 1'b0;
            r_done  <= 1'b0;
            r_error <= 1'b0;
        end else begin
            if (w_clr_done) begin
                r_done <= 1'b0;
            end
            if (w_clr_error) begin
                r_error <= 1'b0;
            end
            if (w_start_valid) begin
                r_done  <= 1'b0;
                r_error <= 1'b0;
            end
            if (w_start_reject) begin
                r_error <= 1'b1;
            end
            r_busy <= w_eng_busy;
            if (w_eng_done) begin
                r_done <= 1'b1;
            end
        end
    end

検証

みんな機能テスト、コードカバレッジ、機能カバレッジなどを頑張ってくれました。
対向のメモリモデルやCPUバスモデルなども単純なものとはいえ、ちゃんと動作しています。
ちゃんとしたRTLが書けるって地味に凄くないですか? いままでそれが出来なかった。

入力信号は立下りで入れるモデルが主流でした。個人的にはそれ嫌いなんですよね。初学者にはそちらの方が分かりやすいとは思いますけれども。

報告書はイマイチなものが多かったです。ライン、トグル等を足して「カバレッジ」として報告するとか、DUT以外のテストベンチもカバレッジに含めるとか。
ここらへん、わかってるところはテンプレやプロンプトでしっかりと押さえれば済みます。
そしてラインカバレッジを表に出させれば、ラインを100%にしようとするんですよね。品質も上がると思います。

【Step0】プロンプトを作る

最近Xで流れている「海外AIガチ勢の最新プロンプト40選」というやつがありまして。
そのなかでもお勧めなのがコレ。

第2章:出口から逆算する ― Outcome-First設計
4. XML Structured Tagging ― 情報をタグで分離する
情報をタグで区切って渡すと、AIの読解精度が上がります。ゴール、背景、制約、参考例、出力形式。これらをひとかたまりの雑文で投げるのではなく、それぞれタグで仕切ってあげる。プロは雑文で投げません。設計図の形で投げます。

コピペ用プロンプト

以下の構造でプロンプトを組みます。
それぞれのタグの中身に沿って回答してください。
<goal>達成すべきゴール</goal>
<context>背景情報</context>
<constraints>禁止事項</constraints>
<examples>参考例</examples>
<output_format>出力形式の枠</output_format>

これ使うと自身の思考整理もつくのでお勧めです。
そんな小技が40個詰まったmdを添えてプロンプトを作らせる。

image.png

添付資料の型で言うと、以下を混ぜるのが筋ええ。
- XML構造化
- Pre-Mortem
- Self-Refine
- Evaluator-Optimizer
- Output-First
単なる「DMA作って」だとモデル差が見えん。
仕様・RTL・検証・報告まで一気通貫で出させるべきや。

ってことでプロンプトを吐いてくれました。(長いので折り畳んでます)

GPT5.5さんが作ってくれたプロンプト
あなたはRTL設計者、検証エンジニア、品質監査者の3役を切り替えて作業してください。

<goal>
VerilogでDMA Controllerを実装し、設計仕様書、検証仕様書、Verilog RTL一式、OSS CAD Suiteを用いた検証環境、カバレッジ測定、検証結果報告書を生成してください。
</goal>

<context>
目的はAIモデルのコーディング性能評価です。
単なるサンプルではなく、設計・実装・検証・報告までの一貫性を評価します。
対象はFPGA/ASIC設計を想定した synthesizable Verilog RTL です。
</context>

<target_design>
DMA ControllerをVerilogで実装してください。

最低要件:

* メモリマップドレジスタ制御
* source address register
* destination address register
* transfer length register
* control register
* status register
* start / busy / done / error
* 32bitデータ幅
* 32bitアドレス幅
* 1クロック同期リセット
* 単一クロックドメイン
* Verilog-2001準拠
* synthesizable RTL
* testbenchから制御可能な簡易バスIFを持つこと

DMA動作:

* startビット書き込みで転送開始
* source addressから読み出し
* destination addressへ書き込み
* length分の32bit wordを転送
* 転送完了でdoneをセット
* 実行中はbusyをセット
* 不正条件ではerrorをセット

簡易メモリモデル:

* testbench内にsource/destinationメモリを用意
* DMAからread/write要求できるインターフェースを定義
</target_design>

<required_outputs>
以下をすべて出力してください。

1. docs/design_spec.md

   * ブロック概要
   * レジスタマップ
   * 信号仕様
   * 状態遷移
   * タイミング仕様
   * エラー条件
   * 制約・前提

2. docs/verification_spec.md

   * 検証方針
   * テスト項目一覧
   * 機能カバレッジ観点
   * コードカバレッジ観点
   * 合否判定基準
   * 既知の制約

3. rtl/

   * dma_ctrl.v
   * dma_regs.v
   * dma_engine.v
   * dma_top.v
   * 必要に応じた補助モジュール

4. tb/

   * tb_dma_top.v
   * 簡易メモリモデル
   * レジスタアクセスtask
   * 正常系テスト
   * length=0などの異常系テスト
   * 連続転送テスト
   * 転送データ一致チェック
   * VCD出力

5. sim/

   * Makefile
   * OSS CAD Suite前提
   * iverilogまたはverilatorで実行可能
   * カバレッジ測定手順を含める
   * 可能ならVerilator coverageを使用

6. reports/verification_report.md

   * 実行環境
   * 実行コマンド
   * テスト結果
   * カバレッジ結果
   * 未達カバレッジ
   * 残課題

7. reports/verification_report.html

   * Markdown版と同等内容のHTML
</required_outputs>

<quality_rules>

* RTLはVerilog-2001で書くこと
* SystemVerilog専用構文は禁止
* unsynthesizableなRTLは禁止
* testbench側ではinitial/task等を使ってよい
* X伝搬を避けるため、全レジスタに明確なリセットを入れる
* ファイルごとにコードブロックを分ける
* 各ファイル名を明記する
* Makefileで build / run / coverage / clean を用意する
* 実際に実行できる構成にする
* 曖昧な仕様は勝手に隠さず、assumptionとして明記する
</quality_rules>

<pre_mortem>
作業開始前に、まず「この成果物が低品質になる原因」を5つ挙げ、それぞれの回避策を1行で示してください。
その回避策を守って以降の成果物を作成してください。
</pre_mortem>

<self_review>
出力後、以下の観点で100点満点の自己採点をしてください。

* 仕様の明確性
* RTLの合成可能性
* テストベンチの網羅性
* OSS CAD Suiteでの再現性
* カバレッジ測定の妥当性
* ドキュメントと実装の整合性

減点理由を明示し、最後に改善版で直すべき点を列挙してください。
</self_review>

<output_format>
以下の順番で出力してください。

1. Pre-Mortem
2. ディレクトリ構成
3. 設計仕様書
4. 検証仕様書
5. RTLコード一式
6. 検証環境コード一式
7. Makefile
8. 検証結果報告書 Markdown
9. 検証結果報告書 HTML
10. 実行手順
11. 自己採点
12. 残課題
</output_format>

<negative_constraints>

* 「簡略化のため省略」は禁止
* 未実装部分をコメントだけで済ませることは禁止
* 疑似コードは禁止
* ファイル名なしのコード出力は禁止
* 検証仕様書とテスト内容の不一致は禁止
* カバレッジ測定手順の省略は禁止
</negative_constraints>

では、上記要件に従って成果物一式を生成してください。

一発で流すため、開発環境用のプロンプトを少し加えてやると良いです。

<context>
…(中略)…
OSS CAD Suiteは"C:\oss-cad-suite\"にあります。
</context>

verilatorでカバレッジ取るのにg++とmake、perlなどを欲しがるので、適度にインストール。

winget install -e --id MSYS2.MSYS2
winget install GnuWin32.Make

【Step1】実装させる

プロンプトをファイルにして、それをドラッグでポイッとしてポチっと。
image.png

.cursorの下にrulesやskillsがあれば、それに沿って進めてくれます。日頃の積み重ねが生きてくるところですね。
社内のコーディングルールですとか、ヘッダコメントのひな形など統一感を出すには大事なぶぶんです。

【Step2】面倒をみる

プロンプトが十分で環境も整っていれば、一発で最後までやってくれると思います。
やれmakeが見つからないだの、環境に関して迷子になるようであればスキルなり<context>にツールの場所や使い方などを明記すれば無駄なトークンを使うことなく進んでくれると思います。
今回はOSS CAD Suiteでお願いしたので、カバレッジ取得周りでのトラブルが多くありました。

Opus4.8 1M(MAX)なんかは自己解決してくれたり選択肢を提示するなどして進めてくれました。
GPT5.4 Mini(Medium)は「make 経由のパス処理が不安定なので、最終的には g++ 直叩きに切り替える方が確実です。」とか色々文句をいってました。

【Step3】出来栄えを眺める

Opus4.8 1M(MAX)

検証結果報告書をHTMLで書かせたので、見やすいです。
色々やってくれたようですね。結構いいんじゃないでしょうか。

image.png

ラインカバレッジ100%到達は偉いですね!
シナリオは10本、27チェック。体感だとブロック作って8割動いてよしよし、ぐらいの感じでしょうか。

カレントに.vcdが出来ていたので開いてみました。
レジスタIF、ReadIF、WriteIFの3つ。
Read/WriteIFはシンプルなハンドシェークで作ってくれました。

image.png

design_spec.md

DMA Controller 設計仕様書

本ドキュメントは、Verilog-2001 で実装した DMA(Direct Memory Access)コントローラ dma_top の設計仕様を定義する。対象は FPGA / ASIC 実装を想定した合成可能 RTL である。メモリマップドレジスタでホストから制御し、ソースアドレスからデスティネーションアドレスへ 32bit word 単位でデータを転送する。

  • 対象 RTL: rtl/dma_top.v, rtl/dma_ctrl.v, rtl/dma_regs.v, rtl/dma_engine.v
  • 準拠: Verilog-2001、単一クロックドメイン、同期リセット(正論理)
  • 検証仕様は verification_spec.md を参照する。

1. ブロック概要

DMA コントローラは次の 4 モジュールで構成する。トップ dma_top は接続階層であり、dma_ctrl がレジスタブロックと転送エンジンを統合する。

モジュール 役割
dma_top 合成トップ。リセット端子 RST を内部 RESET へ接続し dma_ctrl を接続する。
dma_ctrl コア。dma_regsdma_engine を接続する。
dma_regs メモリマップドレジスタ。ホストバスを受け、転送パラメータ保持と状態反映を行う。
dma_engine 転送本体。ステートマシンとデータパスで read/write 転送を実行する。

階層構成:

dma_top
  └─ dma_ctrl
       ├─ dma_regs    (U_DMA_REGS)
       └─ dma_engine  (U_DMA_ENGINE)

データの流れ:ホストが dma_regs にソース/デスト/転送長を設定し、CTRL の START を書き込むと、dma_engine がメモリ I/F を介してソースから 1 word 読み出し、デストへ 1 word 書き込む。これを転送長(word 数)分繰り返す。

2. レジスタマップ

ベースアドレスからのバイトオフセットで定義する。レジスタアドレス幅は 8bit(P_REG_AW)、データ幅は 32bit(P_DATA_W)とする。

オフセット 名称 属性 説明
0x00 SRC RW ソースアドレス(バイトアドレス、4byte アライン必須)
0x04 DST RW デスティネーションアドレス(同上)
0x08 LEN RW 転送長(32bit word 数。0 は不正)
0x0C CTRL W/自己解除 bit0 = START。1 書き込みで転送開始。読み出しは常に 0
0x10 STATUS RO/W1C 状態。bit1/bit2 は 1 書き込みで解除(W1C)
0x14 以降 未定義 - 読み出しは 0 を返す

2.1 CTRL レジスタ(0x0C)

ビット 名称 説明
[0] START 1 を書くと転送開始。1clk のパルスを生成し自動解除する。DONE/ERROR も同時に解除する。
[31:1] 予約 0 を書くこと。読み出しは 0。

2.2 STATUS レジスタ(0x10)

ビット 名称 属性 説明
[0] BUSY RO 転送実行中に 1。
[1] DONE RO/W1C 転送完了で 1(sticky)。1 書き込み、または START で解除。
[2] ERROR RO/W1C エラー検出で 1(sticky)。1 書き込み、または START で解除。
[7:3] 予約 RO 0。
[9:8] ERR_CODE RO エラーコード(下表)。
[31:10] 予約 RO 0。

2.3 ERR_CODE 定義

名称 意味
0 NONE エラー無し
1 LEN0 転送長 0
2 SRC_UNALIGN ソースアドレス非アライン(SRC[1:0] != 0)
3 DST_UNALIGN デストアドレス非アライン(DST[1:0] != 0)

3. 信号仕様

dma_top の入出力ポートを定義する。P_DATA_W=32, P_ADDR_W=32, P_REG_AW=8

3.1 クロック・リセット

信号 方向 説明
CLK in 1 単一クロック。立ち上がりエッジ同期。
RST in 1 同期リセット(正論理)。1 で全 FF を初期化する。

3.2 ホスト側 簡易レジスタバス

信号 方向 説明
REG_WE in 1 書き込みイネーブル(1clk)。
REG_RE in 1 読み出しイネーブル(1clk)。
REG_ADDR in 8 レジスタアドレス(バイト)。
REG_WDATA in 32 書き込みデータ。
REG_RDATA out 32 読み出しデータ。FF 出力(REG_RE の次クロックで確定)。

3.3 メモリ I/F(読み出しチャネル)

信号 方向 説明
MEM_RD_REQ out 1 読み出し要求。ACK まで保持。
MEM_RD_ADDR out 32 読み出しアドレス。
MEM_RD_ACK in 1 読み出し応答(1clk)。
MEM_RD_RDATA in 32 読み出しデータ(ACK と同時に有効)。

3.4 メモリ I/F(書き込みチャネル)

信号 方向 説明
MEM_WR_REQ out 1 書き込み要求。ACK まで保持。
MEM_WR_ADDR out 32 書き込みアドレス。
MEM_WR_WDATA out 32 書き込みデータ。
MEM_WR_ACK in 1 書き込み応答(1clk)。

4. 状態遷移

転送エンジン dma_engine のステートマシン(r_state_st、3bit)を次に定義する。

状態 説明
C_IDLE 0 待機。START を受けて不正条件を判定する。
C_READ 1 ソースから 1 word 読み出す。
C_WRITE 2 デストへ 1 word 書き込む。
C_DONE 3 完了処理(1clk)。

遷移:

        START & 不正条件
   ┌───────────────────────────┐ (ERROR_SET, IDLE 維持)
   v                           │
[C_IDLE] ── START & 正常 ──> [C_READ] ── MEM_RD_ACK ──> [C_WRITE]
   ^                                                        │
   │                              rem>1 (addr+=4, rem-=1)   │
   │                          <─────────────────────────────┤
   │                                                        │ rem==1 & MEM_WR_ACK
   │                                                        v
   └────────────────── 1clk ────────────────────────── [C_DONE]
                                                    (BUSY=0, DONE_SET)
  • C_IDLE: START 受領時に不正条件(第 6 章)を判定する。不正なら ERROR_SET を 1clk 出力し C_IDLE を維持する。正常ならソース/デスト/残数をラッチし BUSY=1 として C_READ へ遷移する。
  • C_READ: MEM_RD_REQ を保持し、MEM_RD_ACK でデータをラッチして C_WRITE へ遷移する。
  • C_WRITE: MEM_WR_REQ を保持し、MEM_WR_ACK で残数を判定する。残数が 1 なら DONE_SET を出力し C_DONE へ、それ以外はアドレスを +4、残数を -1 して C_READ へ戻る。
  • C_DONE: 1clk 後に C_IDLE へ復帰する。
  • default: 未定義状態は C_IDLE へ復帰する(防御的記述。通常到達しない)。

5. タイミング仕様

項目 仕様
レジスタ書き込み REG_WE を 1clk アサート。対象レジスタは当該クロックで更新する。
レジスタ読み出し REG_RE をアサートした次クロックで REG_RDATA が確定する(登録読み、1clk レイテンシ)。
START → BUSY C_IDLESTART を受けた次クロックで BUSY=1 かつ C_READ へ遷移する。
読み出しハンドシェイク MEM_RD_REQ 保持中に MEM_RD_ACK が返るとデータを確定する。待ち状態 0 のとき要求から応答まで実質 1clk。
書き込みハンドシェイク MEM_WR_REQ 保持中に MEM_WR_ACK が返ると 1 word 完了する。
1 word あたり 待ち状態 0 のメモリで read+write 合わせて約 6clk(ハンドシェイク往復を含む)。
DONE 解除 DONE は sticky。STATUS への W1C(bit1=1)または次の START で解除する。
ERROR 解除 ERROR は sticky。STATUS への W1C(bit2=1)または次の START で解除する。

メモリ側の待ち状態(ACK 遅延)は任意であり、エンジンは ACK をハンドシェイクで待つため待ち状態に追従する。

6. エラー条件

C_IDLESTART を受けた時点で以下を判定する。いずれかに該当すると転送を実行せず ERROR をセットする。優先順位は LEN0 > SRC_UNALIGN > DST_UNALIGN とする。

条件 ERR_CODE 動作
LENGTH == 0 1 (LEN0) 転送を行わず ERROR をセット。
SRC[1:0] != 0 2 (SRC_UNALIGN) 転送を行わず ERROR をセット。
DST[1:0] != 0 3 (DST_UNALIGN) 転送を行わず ERROR をセット。

転送中のメモリエラー応答は本仕様の対象外とする(メモリ I/F に error 応答線を持たない)。

7. 制約・前提(Assumptions)

仕様上の前提を明示する。曖昧さを残さないため、以下を確定事項とする。

  • A1. データ幅 32bit、アドレス幅 32bit 固定(パラメータで変更可能だが本仕様は 32bit を前提)。
  • A2. 転送単位は 32bit word のみ。バイトイネーブルや部分転送は持たない。
  • A3. アドレスは 4byte アライン必須。非アラインは第 6 章のエラーとする。
  • A4. LENGTHword 数(バイト数ではない)。LENGTH=0 は不正。
  • A5. 単一クロックドメイン。RST は同期・正論理。全 FF に明示的リセットを持ち、リセット後の X 伝搬を避ける。
  • A6. メモリ I/F は req/ack ハンドシェイク。メモリは要求に対し有限クロックで必ず ACK を返すこと(タイムアウト機構は持たない)。
  • A7. レジスタ読み出しは 1clk レイテンシ(登録読み)。ホストは次クロックで REG_RDATA を取得する。
  • A8. BUSY 中の START 書き込みは無視する(STARTC_IDLE でのみ評価する)。
  • A9. ソースとデストの領域重なりは呼び出し側の責任とする(本コアは重なりを検出しない)。read→write の逐次転送のため、メモリが単一実体でも word 単位では整合する。
  • A10. 割り込み出力は持たない。完了/エラーは STATUS のポーリングで検知する。
  • A11. 合成可能 RTL とし、initial や遅延、SystemVerilog 専用構文は RTL では使用しない。

8. 未実装機能一覧

本仕様の範囲外として、現バージョンで未実装の機能を明示する。

  • バースト転送 / 複数 word 同時転送(現状は 1 word ずつの read→write)。
  • 割り込み(IRQ)出力と割り込みイネーブル。
  • 転送中アボート(中断)機能。
  • メモリエラー応答(error 線)とそのハンドリング。
  • ディスクリプタ方式(スキャッタ/ギャザー)転送。
  • バイト単位 / 非アライン転送。
verification_report.md

DMA Controller 検証結果報告書

本ドキュメントは、DMA コントローラ dma_top の検証実行結果を報告する。設計仕様は ../docs/design_spec.md、検証仕様は ../docs/verification_spec.md を参照する。

1. 実行環境

項目 内容
OS Windows (10.0.26200)
機能シミュレータ Icarus Verilog 12.0(standalone, C:\iverilog)+ vvp
コードカバレッジ Verilator 5.047(OSS CAD Suite 20260409)
C++ ビルド MSYS2 g++ 15.2.0 / GNU Make 4.4.1(mingw32-make)
Verilator ラッパー perl 5.40.3(MSYS2)
波形 GTKWave(OSS CAD Suite, 任意)

Windows ネイティブでは、ツールチェーンの DLL 競合と Verilator 生成 Makefile が Unix シェルを要する点に対応するため、sim/run.ps1 が工程ごとに環境を切り替えて実行する(機能=Icarus、Verilator 生成/集計=OSS CAD Suite、C++ ビルド=MSYS2 bash + mingw64)。POSIX 環境(Linux / WSL / MSYS2 MINGW64)では sim/Makefile をそのまま使用できる。

2. 実行コマンド

Windows(make 不要):

cd dma_controller/sim
powershell -ExecutionPolicy Bypass -File run.ps1 build      # コンパイル
powershell -ExecutionPolicy Bypass -File run.ps1 run        # 機能シミュレーション + 機能カバレッジ
powershell -ExecutionPolicy Bypass -File run.ps1 coverage   # Verilator コードカバレッジ
powershell -ExecutionPolicy Bypass -File run.ps1 clean      # 生成物削除

POSIX 環境(OSS CAD Suite / Linux / WSL):

cd dma_controller/sim
make build      # コンパイル
make run        # 機能シミュレーション + 機能カバレッジ
make coverage   # Verilator コードカバレッジ(line/toggle/branch)
make clean      # 生成物削除

3. テスト結果

機能シミュレーション(run.ps1 run)の結果。全 27 チェック PASS、失敗 0。

ID 項目 結果
T1 レジスタ R/W(SRC/DST/LEN 読み戻し) PASS
T2 正常転送 len=1 + データ一致 PASS
T3 正常転送 len=16 + データ一致(16 word) PASS
T4 異常 len=0(ERR_CODE=1)+ W1C 解除 PASS
T5 異常 src 非アライン(ERR_CODE=2) PASS
T6 異常 dst 非アライン(ERR_CODE=3) PASS
T7 連続転送(len=8 x2)+ データ一致 PASS
T8 待ち状態転送(rd=3/wr=2)+ データ一致 PASS
T9 CTRL=0 読み / IDLE 非 BUSY / 未定義=0 PASS
T10 DONE の W1C 解除 PASS

集計:

 CHECKS = 57, FAILURES = 0
 RESULT : ALL TESTS PASSED
 FUNCTIONAL COVERAGE : 22 / 22 = 100%

4. カバレッジ結果

4.1 機能カバレッジ(テストベンチ実測)

22 観点すべて踏破。22 / 22 = 100%

分類 観点 結果
レジスタ書込 SRC / DST / LEN / CTRL / STATUS 5/5
レジスタ読出 SRC / DST / LEN / CTRL / STATUS 5/5
状態 BUSY / DONE / ERROR 3/3
転送長 LEN=1 / LEN>1 / LEN=0 3/3
エラー LEN0 / SRC_UNALIGN / DST_UNALIGN 3/3
シナリオ 連続転送 / 待ち状態 / データ一致 3/3

4.2 コードカバレッジ(Verilator 実測、RTL のみ)

cov/coverage.dat のポイント種別集計による。

種別 結果 割合
line 38 / 39 97.4%(到達可能行は 100%)
toggle 1420 / 2608 54.4%
verilator_coverage 総合 61 / 217 28.0%

line coverage の未踏 1 件を除けば、到達可能な全行を実行している。verilator_coverage の総合値(28%)は toggle ポイントが支配的なため低く出る。

5. 未達カバレッジ

5.1 line coverage(未踏 1 件)

ファイル 内容 理由
rtl/dma_engine.v 176-177 FSM の default: 分岐(r_state_st <= C_IDLE) 到達不能。状態は 4 値を全列挙しており default には遷移しない。コーディング規約で case への default 付与が必須のため防御的に残置。

→ 到達可能行ベースでは 100%。本分岐は構造上到達しないため未達は妥当(waiver)。

5.2 toggle coverage(54.4%)

未遷移ビットの主因は次のとおりで、いずれも欠陥ではなくテストベクタの値域による構造的要因である。

  • アドレス上位ビット [31:14]:メモリモデルが 16KB(P_AW=12)のため常に 0。
  • 転送長の上位ビット:テストの転送長が小さく上位が遷移しない。
  • データ上位ビット:テストパターン依存で一部のみ遷移。

6. 残課題

  • R1. toggle coverage 向上:制約付きランダムテスト、または 0x00000000 / 0xFFFFFFFF を含む全ビット遷移パターンの追加。アドレス上位は本質的にメモリ深さで制限されるため、メモリモデルのアドレス窓拡大も検討する。
  • R2. メモリエラー応答経路の追加と検証(メモリ I/F へ error 線を追加した場合)。
  • R3. 設計仕様 8 章の未実装機能(バースト / 割り込み / アボート / ディスクリプタ)の実装と対応テスト。
  • R4. CI 化:make coverage を Linux ランナーで自動実行し、カバレッジ閾値で合否判定する。

残課題も出してくれています。

R1. toggle coverage 向上:制約付きランダム、または全ビット遷移パターン(0x00000000 / 0xFFFFFFFF)の追加。アドレス上位はメモリ深さに依存するためメモリ窓拡大も検討。
R2. メモリエラー応答経路の追加と検証(error 線追加時)。
R3. 未実装機能(バースト / 割り込み / アボート / ディスクリプタ)の実装と対応テスト。
R4. CI 化:make coverage を Linux ランナーで自動実行し、閾値で合否判定。

90分で1432.8万トークンを消費し、$15.64(150円換算で3,309円)でした。

GPT 5.5 1M Extra-High

image.png
ちゃんと2portに分かれててえらい。ハンドシェークはReq-Ack式で、バーストは出来ないようですね。

image.png
なぜ英語…
image.png
(これはChromeで翻訳してあります。アウトプットは英語です)
自己評価のスコアってどうやってつけてるんだろう?とは思いますが、定性的にスコアを出せるのなら、エージェントに「XX点になるまで頑張れ」というゴールを与えることが出来るのでわりと重要な概念。

design_spec.md

DMA Controller Design Specification

1. Pre-Mortem

No. 低品質になる原因 回避策
1 バス仕様が曖昧で、テストベンチと RTL のアクセスタイミングが不一致になる 1 クロック要求、次サイクル ACK の簡易同期バスとして固定する
2 DMA のアドレス単位が曖昧で、length とアドレス更新がずれる length は 32bit word 数、アドレスは byte address、1 word ごとに +4 と明記する
3 done/error のラッチとクリア条件が不足し、連続転送で状態が残る start 時に done/error をクリアし、status 書き込みでも個別クリア可能にする
4 メモリ応答の待ち合わせが単純化されすぎ、実行時のハンドシェイクが破綻する read/write request は ACK まで保持し、ACK 受信時だけ状態を進める
5 検証項目と報告書が乖離し、品質評価が追跡できない 検証仕様、テスト名、実行ログ、報告書の項目名を一致させる

2. Block Overview

本 DMA Controller は、メモリマップドレジスタで制御される単一チャネル DMA である。32bit source address から 32bit destination address へ、32bit word 単位で指定 length 分のデータを転送する。

構成ブロックは以下の通り。

Block File Description
Register block rtl/dma_regs.v source/destination/length/control/status レジスタと簡易バス IF
DMA engine rtl/dma_engine.v read/write ハンドシェイク、アドレス更新、転送状態制御
Controller rtl/dma_ctrl.v register block と DMA engine の接続
Top wrapper rtl/dma_top.v 外部ポートを持つ最上位接続階層

3. Assumptions

未指定仕様は以下の前提で固定する。

Item Assumption
Clock domain 単一クロック CLK
Reset RESET=1 の 1 クロック同期リセット
Bus protocol BUS_WR または BUS_RD を 1 クロック以上アサートし、RTL は次クロックで BUS_ACK を 1 クロック返す
Address unit DMA address は byte address、下位 2bit は 32bit word alignment
Transfer length LENGTH は 32bit word 数
Memory response DMA は MEM_RD_REQ/MEM_WR_REQ を ACK まで保持する
Concurrent memory access DMA engine は read と write を同時発行しない
Status clear STATUS の done/error bit に 1 を書くと該当ラッチをクリアする

4. Register Map

Base address は testbench 側で任意に割り当て可能な、DMA 内部 offset とする。

Offset Name R/W Reset Description
0x00 SRC_ADDR R/W 0x00000000 source byte address
0x04 DST_ADDR R/W 0x00000000 destination byte address
0x08 LENGTH R/W 0x00000000 transfer length in 32bit words
0x0C CONTROL W 0x00000000 bit[0] start, write-one pulse
0x10 STATUS R/W1C 0x00000000 bit[0] busy, bit[1] done, bit[2] error

4.1 CONTROL Register

Bit Name Description
0 start 1 書き込みで転送開始要求。読み出し値は 0
31:1 reserved 0 固定

4.2 STATUS Register

Bit Name Description
0 busy DMA engine が転送中の間 1
1 done 転送完了で 1。start または W1C で 0
2 error 不正条件で 1。start または W1C で 0
31:3 reserved 0 固定

5. Signal Specification

5.1 Clock and Reset

Signal Dir Width Description
CLK input 1 rising edge clock
RESET input 1 synchronous active-high reset

5.2 Simple Register Bus

Signal Dir Width Description
BUS_WR input 1 write request
BUS_RD input 1 read request
BUS_ADDR input 8 register byte offset
BUS_WDATA input 32 write data
BUS_RDATA output 32 read data
BUS_ACK output 1 request acknowledge

5.3 Memory Interface

Signal Dir Width Description
MEM_RD_REQ output 1 read request, held until MEM_RD_ACK
MEM_RD_ADDR output 32 read byte address
MEM_RD_DATA input 32 read data valid with MEM_RD_ACK
MEM_RD_ACK input 1 read acknowledge
MEM_WR_REQ output 1 write request, held until MEM_WR_ACK
MEM_WR_ADDR output 32 write byte address
MEM_WR_DATA output 32 write data
MEM_WR_ACK input 1 write acknowledge

6. State Transition

dma_engine の状態は以下の通り。

State Next Condition Next State Action
IDLE start and valid setting READ busy set、内部 source/destination/count load
IDLE start and invalid setting ERROR error pulse
READ MEM_RD_ACK=1 WRITE read data latch
WRITE MEM_WR_ACK=1 and count remains READ address += 4、count -= 1
WRITE MEM_WR_ACK=1 and count last DONE done pulse、busy clear
DONE next clock IDLE pulse clear
ERROR next clock IDLE pulse clear

7. Timing Specification

7.1 Register Write

  1. testbench/master drives BUS_WR=1, BUS_ADDR, BUS_WDATA.
  2. RTL samples the write on rising edge.
  3. RTL asserts BUS_ACK=1 for one clock after sampling.
  4. CONTROL.start write creates one clock START_PULSE.

7.2 Register Read

  1. testbench/master drives BUS_RD=1, BUS_ADDR.
  2. RTL samples the read on rising edge.
  3. RTL asserts BUS_ACK=1 and updates BUS_RDATA.
  4. Master samples BUS_RDATA while BUS_ACK=1.

7.3 DMA Memory Access

  1. Engine asserts MEM_RD_REQ with MEM_RD_ADDR.
  2. Memory returns MEM_RD_ACK and MEM_RD_DATA.
  3. Engine latches read data and asserts MEM_WR_REQ with destination address/data.
  4. Memory returns MEM_WR_ACK.
  5. Engine increments source/destination by 4 and decrements remaining word count.

8. Error Conditions

STATUS.error is set by the following conditions.

Condition Detection
CONTROL.start write while busy=1 dma_regs
Register write to SRC_ADDR/DST_ADDR/LENGTH while busy=1 dma_regs
LENGTH=0 at start dma_engine
SRC_ADDR[1:0] != 0 at start dma_engine
DST_ADDR[1:0] != 0 at start dma_engine

9. Constraints

  • RTL は Verilog-2001 準拠の synthesizable subset で記述する。
  • RTL 内では initial、delay、task、force、SystemVerilog 構文を使わない。
  • すべての FF は同期リセットで初期化する。
  • 単一クロックドメインのみを対象とし、CDC は存在しない。
  • メモリモデルの範囲外アクセスは testbench 側の検証対象外とする。
verification_report.md

DMA Controller Verification Report

1. Execution Environment

Item Result
OS Windows 10.0.26200
Workspace c:\sandbox\DMAC
OSS CAD Suite C:\oss-cad-suite\
Icarus Verilog C:\oss-cad-suite\bin\iverilog.exe
Verilator C:\oss-cad-suite\bin\verilator_bin.exe, version 5.047 devel
MSYS64 toolchain C:\msys64\mingw64\bin\g++.exe, C:\msys64\usr\bin\uname.exe
Coverage driver sim/coverage.cmd (make coverage から呼び出し)

2. Executed Commands

cd c:\sandbox\DMAC\sim
make run
make coverage

make coverage は内部で coverage.cmd を実行し、MSYS64 の g++/uname と OSS CAD Suite の Verilator/python を PATH に追加してビルドする。

3. Test Results

make run passed with no compile warnings.

Test ID Name Result
TC001 Reset and register default PASS
TC002 Normal transfer PASS
TC003 Length zero error PASS
TC004 Back-to-back transfer PASS
TC005 Unaligned address error PASS
TC006 Busy protection PASS

Simulation summary:

Item Result
Total checks 35
Error count 0
Final message TEST PASS
VCD sim/work/tb_dma_top.vcd

Functional coverage counters from testbench:

Coverage Item Hit Count
Reset sequence 1
Normal transfer 1
Length zero error 1
Back-to-back transfer 1
Unaligned address error 1
Busy protection error 1
Compared words 26

4. Verilator Result

make coverage completed successfully with MSYS64 toolchain integration.

Generated artifacts:

Artifact Path
Coverage database sim/work/coverage.dat
Annotated sources sim/work/annotated/
DUT line summary sim/work/dut_line_coverage.txt
Overall summary sim/work/coverage_summary.txt

5. Coverage Results

Coverage Type Result
Functional coverage All planned functional bins hit
Verilator simulation PASS
Overall Verilator coverage points 118 / 453 = 26.00% (TB とランタイム含む全体)
DUT line coverage (RTL only) 100 / 120 = 83.33%

5.1 DUT Line Coverage by Module

Module Covered Uncovered Total Line %
dma_regs.v 50 16 66 75.76%
dma_engine.v 39 4 43 90.70%
dma_ctrl.v 9 0 9 100.00%
dma_top.v 2 0 2 100.00%
DUT TOTAL 100 20 120 83.33%

5.2 Main Uncovered Lines

Module Uncovered behavior
dma_regs.v busy 中の SRC_ADDR/DST_ADDR 書き込みエラー分岐、未定義 offset への write/read default 分岐、各レジスタ read パス
dma_engine.v default 状態遷移(不正 state からの復帰)

6. Unreached Coverage

Item Reason
dma_regs read 系 case 分岐 testbench は主に write と STATUS read のみ使用
dma_regs busy 中アドレス/length 書き込みエラー TC006 は LENGTH/CONTROL のみ再書き込み
dma_engine default 状態 正常テストでは不正 state に遷移しない
Memory range error path メモリ範囲外アクセスは設計前提外
TB / Verilator runtime coverage points DUT 以外を含むため全体率は 26.00% と低く見える

7. Remaining Issues

  • dma_regs の read パスと busy 中レジスタ書き込みエラーを追加テストでカバーする。
  • Add optional memory range checking if the real integration requires address window protection.
  • Add interrupt output and descriptor/chained transfer support only if required by the target SoC specification.

8. Self Review

Viewpoint Score Reason
Specification clarity 92 Register map, timing, error conditions, assumptions are explicit
RTL synthesizability 90 RTL avoids SystemVerilog and unsynthesizable constructs; synthesis tool run is not included
Testbench coverage 86 Required normal/error/continuous tests are present; randomized and stress tests are not included
OSS CAD reproducibility 90 Icarus run and MSYS64-backed Verilator coverage are reproducible via make coverage
Coverage validity 85 DUT line coverage 83.33% obtained; read path and default state remain partially uncovered
Doc/implementation consistency 90 Test IDs, register map, status behavior, and RTL are aligned

Overall score: 88 / 100

Deduction reasons:

  • dma_regs line coverage is 75.76% because read paths and some busy-error branches are not exercised.
  • Memory range error handling is not implemented because the design assumption limits memory model scope.
  • Testbench is directed-test based and does not include randomized wait-state insertion.

Improvement items for the next revision:

  • Add register read-back tests and busy-period SRC_ADDR/DST_ADDR write tests.
  • Add randomized memory ACK latency to test request hold behavior more strongly.
  • Add destination overwrite protection or address range checks if system-level requirements demand it.
  • Add synthesis lint with Yosys or the target ASIC/FPGA lint flow.

15分で342.1万トークンを消費し、$4.01(150円換算で602円)でした。

GPT-5.4 Mini Medium

cursorでトークンのコストでComposer以外から選ぶならここかな?というモデルです。

image.png
一見それっぽいんですが、MEM I/Fはそれじゃない感。1portでRead-Writeを繰り返す設計です。
BRAMの中でアドレスA➡アドレスBへ移動という感じ?
ステータスとしてBUSY/DONE/ERRORがあるんですが、BUSYの立下りとDONEの立ち上がりの間に1クロック隙間が空いているのはバグまたは仕様不備でしょう。

image.png
カバレッジ結果もよくわかりませんね。ブランチは119/2512(4.7%)で低いです。ラインの結果もレポートには無し。HTML自体も見た目に物足りなさが残ります。

残課題出してくれてますが、もっといろいろあるよね感。

メモリ wait state を持つ TB を追加し、MEM_READY=0 の分岐を検証する。
32-bit アドレス overflow の異常系を追加する。
design_spec.md

DMA Controller 設計仕様書

本ドキュメントは、32-bit 幅の DMA Controller の RTL 実装仕様を定義する。メモリマップドレジスタ、単一クロック同期動作、テストベンチから制御可能な簡易バス IF、および DMA エンジンの動作を対象とする。

1. ブロック概要

DMA Controller は、CPU 代替のテストベンチからレジスタを書き込み、ソースアドレスからデスティネーションアドレスへ 32-bit word 単位で連続転送する。

ブロック 役割
dma_regs source address register, destination address register, transfer length register を保持する。
dma_ctrl start, busy, done, error を管理する。control register と status register を提供する。
dma_engine メモリ read/write シーケンスを実行する。
dma_top バス IF、レジスタブロック、DMA エンジンを接続する。

1.1 前提

項目 内容
クロック 単一クロックドメインとする。
リセット 同期アクティブ High の 1 クロック同期リセットとする。
データ幅 32-bit 固定とする。
アドレス幅 32-bit 固定とする。
転送単位 1 word = 32-bit = 4 byte とする。
簡易バス TB が同期的に BUS_WE / BUS_RE を制御する単純なレジスタバスとする。
メモリ IF DMA は read request と write request を 1 本の同期 IF で発行する。

2. レジスタマップ

アドレスは byte アドレスとし、4 byte 境界に配置する。

Address Register Access Bit field 内容
0x00 source address register R/W [31:0] 読み出し元の先頭アドレス。
0x04 destination address register R/W [31:0] 書き込み先の先頭アドレス。
0x08 transfer length register R/W [31:0] 転送する word 数。
0x0C control register W [0] START, [1] CLR_DONE, [2] CLR_ERROR start 要求と status クリア要求。読み出し値は 0 とする。
0x10 status register R [0] BUSY, [1] DONE, [2] ERROR 現在状態を返す。

2.1 control register の動作

Bit 名称 動作
[0] START 1 を書き込んだ 1 クロック後に start pulse を生成する。busy 中は start せず error をセットする。
[1] CLR_DONE 1 を書き込むと done を 0 にする。
[2] CLR_ERROR 1 を書き込むと error を 0 にする。

2.2 status register の動作

Bit 名称 動作
[0] BUSY DMA エンジンが転送実行中のとき 1
[1] DONE 転送完了後に 1 を保持する。
[2] ERROR 異常終了時に 1 を保持する。

DONE と ERROR は sticky とし、明示的な clear または reset で解除する。

3. 信号仕様

3.1 バス IF

信号 方向 内容
CLK 入力 1 システムクロック。立上りエッジで動作する。
RESET 入力 1 同期リセット。High で状態とレジスタを初期化する。
BUS_ADDR 入力 32 レジスタアドレス。byte アドレス。
BUS_WDATA 入力 32 レジスタ書き込みデータ。
BUS_WE 入力 1 書き込み要求。
BUS_RE 入力 1 読み出し要求。
BUS_RDATA 出力 32 読み出しデータ。組み合わせで返す。
BUS_READY 出力 1 常に 1 とする。TB は 1 クロック同期アクセスを行う。

3.2 メモリ IF

信号 方向 内容
MEM_REQ 出力 1 DMA がメモリ要求を発行中であることを示す。
MEM_WE 出力 1 1 で write request、0 で read request。
MEM_ADDR 出力 32 メモリアドレス。
MEM_WDATA 出力 32 write request のデータ。
MEM_READY 入力 1 メモリ側の応答可能通知。 1 のとき 1 サイクル内で受諾済みとする。
MEM_RDATA 入力 32 read request の戻りデータ。

3.3 ステータス信号

信号 方向 内容
BUSY 出力 1 実行中状態。
DONE 出力 1 完了状態。
ERROR 出力 1 異常状態。

4. 状態遷移

DMA エンジンは以下の状態を持つ。

状態 説明
IDLE start 待ち。
READ_REQ source address から 1 word を読み出す。
WRITE_REQ destination address へ 1 word を書き込む。

4.1 遷移条件

現在状態 条件 次状態 動作
IDLE start 受理かつ length > 0 かつ source/destination が 4 byte aligned READ_REQ busy を 1 にし、転送初期値をラッチする。
IDLE start 受理かつ length = 0 IDLE error を 1 にする。
IDLE start 受理かつ未整列アドレス IDLE error を 1 にする。
READ_REQ MEM_READY = 1 WRITE_REQ read data をラッチする。
WRITE_REQ MEM_READY = 1 かつ remaining = 1 IDLE busy を 0、done を 1 にする。
WRITE_REQ MEM_READY = 1 かつ remaining > 1 READ_REQ src/dst を +4、remaining を -1 する。

5. タイミング仕様

5.1 バスアクセス

項目 仕様
書き込み BUS_WE = 1 を 1 クロック有効にする。 BUS_ADDRBUS_WDATA はその立上りエッジでサンプルする。
読み出し BUS_RE = 1 を 1 クロック有効にする。 BUS_RDATA は組み合わせ出力とし、同一サイクルで参照する。
応答 BUS_READY = 1 固定とする。ウェイト状態は持たない。

5.2 DMA 転送

項目 仕様
start pulse control register に START を書いた 1 クロック後に有効化する。
1 word 転送 READ_REQWRITE_REQ を 1 word ごとに 1 回ずつ発行する。
busy start 受理から最終 write 完了まで 1
done 最終 write 完了サイクルの後に 1 を保持する。

6. エラー条件

条件 エラー動作
length = 0 error を 1 にする。busy は 0 を保持する。
source address 未整列 error を 1 にする。
destination address 未整列 error を 1 にする。
busy 中に start 要求 error を 1 にする。
未定義アドレスへの bus read/write 0 を返す、または無視する。エラー扱いにはしない。

7. 制約・前提

項目 制約
RTL 言語 Verilog-2001 準拠とする。
合成性 initialtask$display は TB のみで使用する。
reset 全レジスタを同期リセットで明確に初期化する。
address increment 1 word ごとに +4 とする。
overflow 32-bit 加算の桁あふれは未検出とする。
mem model TB の簡易メモリは ready=1 の単純応答を基本とする。
verification_report.md

DMA Controller 検証結果報告書

本報告書は、dma_top のシミュレーション結果、確認したテスト項目、カバレッジ観点、残課題をまとめる。

1. 実行環境

項目 内容
OS Windows 10.0.26200
シミュレーション Icarus Verilog 14.0
OSS CAD Suite C:\oss-cad-suite\
対象 rtl/*.v, tb/tb_dma_top.v

2. 実行コマンド

2.1 機能検証

iverilog -g2001 -Wall -o sim\tb_dma_top.vvp tb\tb_dma_top.v rtl\dma_ctrl.v rtl\dma_engine.v rtl\dma_regs.v rtl\dma_top.v
vvp sim\tb_dma_top.vvp

2.2 カバレッジ検証

verilator_bin.exe --cc --timing --coverage -Wno-fatal --top-module tb_dma_top -Wall --Mdir obj_dir tb\tb_dma_top.v rtl\dma_ctrl.v rtl\dma_engine.v rtl\dma_regs.v rtl\dma_top.v
obj_dir\Vtb_dma_top.exe
verilator_coverage_bin_dbg.exe --write-info coverage.info coverage.dat

coverage.datcoverage.info を生成できたため、Verilator のコードカバレッジは取得済みである。

3. テスト結果

ID テスト名 結果
T01 reset 初期化 PASS
T02 1 word 転送 PASS
T03 複数 word 転送 PASS
T04 length=0 異常 PASS
T05 未整列アドレス異常 PASS
T06 連続転送 PASS
T07 status クリア PASS
T08 転送データ一致 PASS

シミュレーション終端時点で ERROR_COUNT=0 を確認した。

4. カバレッジ結果

区分 結果
機能カバレッジ 8/8 のテスト項目を実行した。
コードカバレッジ coverage.datcoverage.info を生成した。
Verilator coverage 実行済み。

4.1 実行済みの機能観点

  • reset 後の初期化
  • 1 word 転送
  • 8 word 転送
  • length=0 の error
  • 未整列アドレスの error
  • 連続転送
  • status clear
  • source/destination のデータ一致確認

4.2 coverage 集計

  • coverage.info による集計で、branch hit / total は 119 / 2512 であった。
  • 未達の多くは Verilator 生成コード由来の内部分岐であり、設計機能として残っている主な未達は wait state 分岐と 32-bit overflow 分岐である。

5. 未達カバレッジ

項目 理由
wait state 分岐 TB の簡易メモリは ready=1 固定としている。
32-bit overflow 仕様上、桁あふれは未検証とした。

6. 残課題

  • メモリ wait state を持つ TB を追加し、MEM_READY=0 の分岐を検証する。
  • 32-bit アドレス overflow の異常系を追加する。

60分で1807.0万トークンを消費し、$6.82(150円換算で1,023円)でした。

Composer 2.5

cursorにおけるComposerのコスト安は異常。
1.xはあまり役に立ちませんでしたが、2.0以降は使い勝手が良いです。早いし。
コーディングはそこまで得意ではないかな?ツール回りは結構得意かもしれない。

image.png
おまえも1port派かー。MEM_ACKってなんじゃ。
でもまあ動いてるし、超早いしトークン消費も少ない。
とにかく軽快なのはいい。fastじゃなくてもこれだけ早いのは素晴らしい。
環境の迷いが少なかったかな。Opus4.8は深く考えすぎるが故に、「WSLとDocker両方検討したんだけどWSLはroot権限がないしDockerは立ち上がってないみたい。どうする?」とか、いい提案だとはおもうけどそこにトークン(工数)使ったかー、って感じはあった。

design_spec.md

DMA Controller 設計仕様書

本ドキュメントは、32bit 同期リセット・単一クロックドメインの DMA Controller の設計仕様を定義する。

1. ブロック概要

DMA Controller は、CPU(またはテストベンチ)からのレジスタアクセスにより転送パラメータを設定し、外部メモリインターフェースを通じてソースアドレスからデータを読み出し、デスティネーションアドレスへ書き込む。

1.1 モジュール構成

モジュール ファイル 役割
dma_top rtl/dma_top.v トップレベル。バス IF とメモリ IF を外部へ露出
dma_ctrl rtl/dma_ctrl.v レジスタブロックとエンジンの統合、エラー判定
dma_regs rtl/dma_regs.v メモリマップドレジスタ、簡易バス IF
dma_engine rtl/dma_engine.v 読み出し/書き込みシーケンサ(FSM)

1.2 データフロー

[Bus Master] --(reg bus)--> [dma_regs] --params--> [dma_engine] --(mem bus)--> [Memory]
                                  ^                      |
                                  +-------- status -------+

2. レジスタマップ

アドレスはバイトアドレス。下位 2bit はワードアライメント前提で無視しない(32bit アクセス単位)。

オフセット 名称 R/W 説明
0x00 SRC_ADDR R/W ソース開始アドレス(バイトアドレス、4byte アライメント推奨)
0x04 DST_ADDR R/W デスティネーション開始アドレス
0x08 LENGTH R/W 転送ワード数(32bit ワード単位。1 = 4byte)
0x0C CTRL R/W 制御レジスタ(下表参照)
0x10 STATUS RO ステータスレジスタ(下表参照)

2.1 CTRL レジスタ(0x0C)

Bit 名称 属性 説明
0 START W1P 1 書き込みで転送開始。内部で 1 サイクルパルス化
1 CLR_DONE W1P 1 書き込みで DONE ビットをクリア
2 CLR_ERROR W1P 1 書き込みで ERROR ビットをクリア
31:3 - - 常に 0 読み出し

2.2 STATUS レジスタ(0x10)

Bit 名称 説明
0 BUSY 転送実行中
1 DONE 転送正常完了
2 ERROR エラー発生
31:3 - 常に 0

3. 信号仕様

3.1 トップレベル(dma_top)

信号 方向 説明
CLK in 1 システムクロック(立上りエッジ)
RESET in 1 同期リセット(Active High)
BUS_ADDR in 32 レジスタバスアドレス
BUS_WDATA in 32 レジスタバス書き込みデータ
BUS_RDATA out 32 レジスタバス読み出しデータ
BUS_WR_EN in 1 レジスタバス書き込みイネーブル
BUS_RD_EN in 1 レジスタバス読み出しイネーブル
MEM_ADDR out 32 メモリアドレス
MEM_WDATA out 32 メモリ書き込みデータ
MEM_RD_EN out 1 メモリ読み出し要求
MEM_WR_EN out 1 メモリ書き込み要求
MEM_RDATA in 32 メモリ読み出しデータ
MEM_ACK in 1 メモリ応答(1 サイクルパルス)

3.2 簡易バスプロトコル

  • 1 クロックで読み/書き完了(ウェイトステートなし)
  • BUS_WR_EN または BUS_RD_EN が High のサイクルでアクセス成立
  • BUS_RDATA は読み出しサイクルの CLK 立上り後に有効

3.3 メモリプロトコル

  • マスタは MEM_RD_EN または MEM_WR_EN を 1 サイクル以上アサート
  • スレーブは要求検出後、次の CLK 立上りで MEM_ACK を 1 サイクルアサート
  • エンジンは r_mem_req_active により要求と応答を分離し、誤 ACK 取り込みを防止

4. 状態遷移

4.1 dma_engine FSM

状態 説明
IDLE 0 待機
READ 1 ソース読み出し
WRITE 2 デスティネーション書き込み
DONE 3 完了処理(1 サイクル)
IDLE --(START)--> READ --(ACK)--> WRITE --(ACK, count>1)--> READ
                                  |
                                  +--(ACK, count==1)--> DONE --> IDLE

各 READ/WRITE 状態は「要求発行」→「ACK 待ち」の 2 フェーズで動作する。

5. タイミング仕様

項目 仕様
クロック 単一 CLK、立上りエッジ動作
リセット 同期、RESET=1 で全 FF を初期化
転送単位 32bit(4byte)
アドレス更新 各ワード転送後に SRC/DST とも +4
BUSY エンジンが IDLE 以外、または DONE 処理中
DONE 全ワード転送完了後にセット。CLR_DONE または新規有効 START でクリア

6. エラー条件

条件 ERROR 転送動作
LENGTH=0 で START セット 開始しない
BUSY 中に START セット 進行中転送は継続、再始動しない
メモリ範囲外アクセス 未検出 本実装ではスレーブ側で無視(Assumption)

7. 制約・前提(Assumption)

  1. アドレスアライメント: SRC_ADDR / DST_ADDR は 4byte 境界を推奨。非アライメント時の動作は保証しない。
  2. LENGTH 単位: 32bit ワード数。バイト数ではない。
  3. BUSY 中レジスタ書き込み: SRC/DST/LENGTH は BUSY 中は更新不可。CTRL は常に書き込み可能。
  4. メモリモデル: テストベンチでは 1 サイクル ACK。本番環境では ACK タイミングに合わせエンジンが待機する。
  5. 同時読書き: メモリバスは単一ポート。DMA は読みと書きを同時に行わない。
  6. エラー復帰: ERROR は CLR_ERROR または有効な START 受付時にクリア可能。
  7. Verilog-2001: SystemVerilog 専用構文は使用しない。全 FF に同期リセットを実装。
verification_report.md

DMA Controller 検証結果報告書

1. 実行環境

項目 内容
OS Windows 10 (10.0.26200)
シミュレータ Icarus Verilog 14.0 / Verilator 5.047 (OSS CAD Suite)
ビルド MSYS2 MinGW (C:\msys64\mingw64\bin\make, g++)
パス C:\oss-cad-suite\
対象 RTL Verilog-2001、同期リセット
日付 2026-06-10

2. 実行コマンド

2.1 Icarus Verilog(機能シミュレーション)

$env:PATH = "C:\oss-cad-suite\bin;C:\oss-cad-suite\lib;" + $env:PATH
cd c:\sandbox\DMAC\sim
iverilog -g2005 -o work/tb_dma_top_iv.out -I../rtl -I../tb `
  ../rtl/dma_regs.v ../rtl/dma_engine.v ../rtl/dma_ctrl.v ../rtl/dma_top.v `
  ../tb/simple_mem.v ../tb/coverage_mon.v ../tb/tb_dma_top.v
vvp work/tb_dma_top_iv.out

2.2 Makefile ターゲット

cd sim
make build    # iverilog コンパイル
make run      # シミュレーション実行
make coverage # Verilator カバレッジ(MSYS2 bash 推奨、下記参照)
make clean    # work/ 削除

2.3 Verilator カバレッジ(MSYS2 bash)

C:\msys64\usr\bin\bash.exe -lc "
  export PATH=/usr/bin:/mingw64/bin:/c/oss-cad-suite/bin:\$PATH
  export VERILATOR_ROOT=/c/oss-cad-suite/share/verilator
  cd /c/sandbox/DMAC/sim && make coverage
"

2.4 PowerShell ラッパー

powershell -ExecutionPolicy Bypass -File sim/run_sim.ps1 -Target run

3. テスト結果

ID テスト名 結果
T1 単語転送 PASS
T2 8 語転送 PASS
T3 length=0 エラー PASS
T4 busy 中 start PASS
T5 連続転送 PASS
T6 レジスタ読み出し PASS

集計: pass=21, fail=0 — ALL TESTS PASSED

3.1 ログ抜粋

=== SUMMARY: pass=21 fail=0 ===
ALL TESTS PASSED
COVERAGE: functional points 10/10 (100%)

4. カバレッジ結果

4.1 機能カバレッジ

観点 結果
エンジン FSM(IDLE/READ/WRITE/DONE) ヒット
STATUS(BUSY/DONE/ERROR) ヒット
START パルス ヒット
メモリ RD/WR ヒット
合計 10/10 (100%)

4.2 コードカバレッジ(Verilator)

項目 結果
ビルド・シミュレーション 成功(sim/work/verilator/Vtb_dma_top.exe
テスト pass=21, fail=0
RTL 行カバレッジ 429/566 (75.0%)
注記 GnuWin32 の make 単体では不可。MSYS2 の make + g++ + bash が必要

未ヒット行(RTL):

  • dma_regs.v: CTRL レジスタ読み出し、未定義アドレス読み出し
  • dma_engine.v: O_ERRORdefault 状態
  • dma_ctrl.v: 未使用信号 w_eng_error

5. 未達カバレッジ

種別 未達項目 理由
コード行 dma_engine default 状態 到達不能防御コード
コード行 dma_regs 未定義アドレス読み出し TB 未実施
コード行 メモリ範囲外エラー検出 仕様上未実装
トグル 一部信号 未使用ポート・防御コード

6. 残課題

  1. 未定義アドレス読み出しテスト追加(行カバレッジ向上)
  2. メモリアクセスエラー検出と ERROR 連動
  3. BUSY 中 CTRL.START 以外のレジスタ保護ポリシー見直し
  4. AXI/AHB 等の標準バス IF への置き換え
  5. 合成・タイミング検証(FPGA/ASIC ターゲット向け)

20分で537.7万トークンを消費し、$2.61(150円換算で392円)でした。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?