前置き
世はバイブコーディング時代
最近は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などでしっかり指定したものはその通りにやってくれるしチェックもしてくれました。
ふわっとした部分は高価なモデルほどマシ。趣味なら札束で叩くのもいいと思います。でもそれってガチャなんですよね。
仕事なら毎回おなじ結果が出るようskillsやrulesをしっかり整備してやるのが大事。
そうすればローカルLLMでも十分な成果が得られるように思います。
RTL実装
総じて微妙です。RTLじゃないんだよなぁというか。どうもソフトっぽい書き方になりますね…。
パッと見のコードとしてはそんなに悪くないんです。でも、やはりちょいちょいHDLとして許したくない記述がまだまだ出てきます。
厄介なのは、それなりに動いちゃうってことです。
微妙だと感じる記述は、ツールやシードによって解釈が変わったりバグを誘発しやすい記述なわけで、8割方問題ないけど残り2割で大炎上し徹夜する羽目になった、みたいなやつです。
こういったところを、いかにして.cursorのskillsやrulesで押さえていくかが重要そう。
たとえばこういうコードです。
// 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を添えてプロンプトを作らせる。
添付資料の型で言うと、以下を混ぜるのが筋ええ。
- 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】実装させる
プロンプトをファイルにして、それをドラッグでポイッとしてポチっと。

.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で書かせたので、見やすいです。
色々やってくれたようですね。結構いいんじゃないでしょうか。
ラインカバレッジ100%到達は偉いですね!
シナリオは10本、27チェック。体感だとブロック作って8割動いてよしよし、ぐらいの感じでしょうか。
カレントに.vcdが出来ていたので開いてみました。
レジスタIF、ReadIF、WriteIFの3つ。
Read/WriteIFはシンプルなハンドシェークで作ってくれました。
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_regs と dma_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_IDLE で START を受けた次クロックで 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_IDLE で START を受けた時点で以下を判定する。いずれかに該当すると転送を実行せず 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.
LENGTHは word 数(バイト数ではない)。LENGTH=0は不正。 - A5. 単一クロックドメイン。
RSTは同期・正論理。全 FF に明示的リセットを持ち、リセット後の X 伝搬を避ける。 - A6. メモリ I/F は req/ack ハンドシェイク。メモリは要求に対し有限クロックで必ず ACK を返すこと(タイムアウト機構は持たない)。
- A7. レジスタ読み出しは 1clk レイテンシ(登録読み)。ホストは次クロックで
REG_RDATAを取得する。 - A8.
BUSY中のSTART書き込みは無視する(STARTはC_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

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

なぜ英語…

(これは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
- testbench/master drives
BUS_WR=1,BUS_ADDR,BUS_WDATA. - RTL samples the write on rising edge.
- RTL asserts
BUS_ACK=1for one clock after sampling. -
CONTROL.startwrite creates one clockSTART_PULSE.
7.2 Register Read
- testbench/master drives
BUS_RD=1,BUS_ADDR. - RTL samples the read on rising edge.
- RTL asserts
BUS_ACK=1and updatesBUS_RDATA. - Master samples
BUS_RDATAwhileBUS_ACK=1.
7.3 DMA Memory Access
- Engine asserts
MEM_RD_REQwithMEM_RD_ADDR. - Memory returns
MEM_RD_ACKandMEM_RD_DATA. - Engine latches read data and asserts
MEM_WR_REQwith destination address/data. - Memory returns
MEM_WR_ACK. - 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_regsline 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_ADDRwrite 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以外から選ぶならここかな?というモデルです。

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

カバレッジ結果もよくわかりませんね。ブランチは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_ADDR と BUS_WDATA はその立上りエッジでサンプルする。 |
| 読み出し |
BUS_RE = 1 を 1 クロック有効にする。 BUS_RDATA は組み合わせ出力とし、同一サイクルで参照する。 |
| 応答 |
BUS_READY = 1 固定とする。ウェイト状態は持たない。 |
5.2 DMA 転送
| 項目 | 仕様 |
|---|---|
| start pulse | control register に START を書いた 1 クロック後に有効化する。 |
| 1 word 転送 |
READ_REQ と WRITE_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 準拠とする。 |
| 合成性 |
initial、task、$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.dat と coverage.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.dat と coverage.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以降は使い勝手が良いです。早いし。
コーディングはそこまで得意ではないかな?ツール回りは結構得意かもしれない。

おまえも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)
- アドレスアライメント: SRC_ADDR / DST_ADDR は 4byte 境界を推奨。非アライメント時の動作は保証しない。
- LENGTH 単位: 32bit ワード数。バイト数ではない。
- BUSY 中レジスタ書き込み: SRC/DST/LENGTH は BUSY 中は更新不可。CTRL は常に書き込み可能。
- メモリモデル: テストベンチでは 1 サイクル ACK。本番環境では ACK タイミングに合わせエンジンが待機する。
- 同時読書き: メモリバスは単一ポート。DMA は読みと書きを同時に行わない。
- エラー復帰: ERROR は CLR_ERROR または有効な START 受付時にクリア可能。
- 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_ERROR、default状態 -
dma_ctrl.v: 未使用信号w_eng_error
5. 未達カバレッジ
| 種別 | 未達項目 | 理由 |
|---|---|---|
| コード行 | dma_engine default 状態 |
到達不能防御コード |
| コード行 | dma_regs 未定義アドレス読み出し | TB 未実施 |
| コード行 | メモリ範囲外エラー検出 | 仕様上未実装 |
| トグル | 一部信号 | 未使用ポート・防御コード |
6. 残課題
- 未定義アドレス読み出しテスト追加(行カバレッジ向上)
- メモリアクセスエラー検出と ERROR 連動
- BUSY 中 CTRL.START 以外のレジスタ保護ポリシー見直し
- AXI/AHB 等の標準バス IF への置き換え
- 合成・タイミング検証(FPGA/ASIC ターゲット向け)
20分で537.7万トークンを消費し、$2.61(150円換算で392円)でした。


