Ollama cloud経由でdeepseek-v4 を使って,FPGAデザインを作ります.使うボードはTang Mega 138K Pro Dock(Sipeed)です.HDMIコネクタ(TX)からDVI信号で,画面データを出力します.
deepseek-v4を使った感想として,「細かい仕様を具体的に与えれば,それにそったRTLコード生成はできそう」といった感じです.人間の意図を読み取る力は低いので,曖昧だったり,抽象的なプロンプトには弱いかもしれません.

●開発環境
次のソフトウェアを使います.なんとWindows11でやります.
- Ollama(ローカルLLMではなく,課金ありのクラウドLLMをOllama cloudから使う)
- VSCode
- VS Code Ollama拡張機能
- Copilot Agent(VSCode拡張機能)
- deepseek-v4(Ollama経由)
- Gowin EDA
Ollamaをインストールすれば,プロンプトを与えて,コード生成させられます.しかし,Ollamaが提供するローカルアプリケーションはローカルのファイルシステムにアクセスできません.ファイルの保存や編集ができないので,生成されたテキストを統合開発環境などへコピペするしかありません.当然,エージェントループが組めないので,AIエージェントを使う価値があまりありません.
本稿では,VSCodeとCopilot Agentを使ってOllamaの機能を呼び出します.Copilot Agentはローカルシステムにアクセスできるので,ファイル保存や編集に加えコマンドも実行できます.
これにより,AIエージェントはローカルPC内のGowin EDAを使って合成したり,合成によるエラーメッセージを取り込んで自動でRTLコードを修正したりできるようになります.
●環境構築
▲インストール
VSCodeをインストールします.
Ollamaをインストールします(OllamaSetup.exe).
VSCode上で,Ollama Copilot VSCode 拡張機能をインストールします.
Gowin EDAをインストールします(ライセンス申請して,ライセンスを適用する).
OllamaのProプラン($20/月)を契約します.
ローカルPCのOllamaアプリケーションを起動し,ログインします.
▲ローカルPC上でdeepseek-v4へアクセスするOllamaを動かす
ローカルPCのコマンドプロンプトで ollama run deepseek-v4-pro:cloud を実行します.
http://localhost:11434 で,Ollamaにアクセスできるようになります.このURIは,VSCodeにOllama拡張機能をインストールした段階で自動的に設定されるので,手動で記述する必要はありません.
▲VSCode上で使うエージェントを設定
VSCodeのChatウィンドウでアクセス先を選択します.ローカルPCでOllamaを動かしているので,ローカルを選択します.

VSCodeのChatウィンドウの下方で,モデルとしてdeepseek V4.0 Proを選択します.

●RTLコード生成の要点
▲ポイント1
Gowin EDAでは,IPの生成はTclなどのCLIではできず,統合開発環境を起動してGUIで行う必要があります.従って,AIエージェントに対して事前に,「ベンダのIPを使ってもよいが,IPの生成作業は人間が行うので,設定パラメータのみを示す」ように指示しておく必要があります.これによってAIエージェントは,RTLコードを生成した後,「IPを生成してください.IPが準備できたら合成するので教えてください」と通知してきます.
▲ポイント2
PLLの周波数設定がうまくできず,同じやり直しを永遠に繰り返していました.埒が明かないので,ピクセルクロック74.25MHzとシリアルクロック371.25MHzという周波数のみを指定し,PLLは人間が用意する旨を指示しました.
●生成されたRTLコード
・全体構成
tang_mega_138k_dvi/
├── src/
│ ├── top.sv … トップモジュール
│ ├── GOWIN_PLL … IPジェネレータで手動生成
│ ├── Gowin_CLKDIV … IPジェネレータで手動生成
│ ├── vga_timing.sv … タイミングジェネレータ (800x600@60Hz)
│ ├── color_bar.sv … 7色カラーバー生成
│ ├── tmds_encoder.sv … TMDSエンコーダ
│ └── DVI_TX_TOP … DVI送信 IPジェネレータで手動生成
└── constraints/
└── tang_mega_138k_pro.cst・top.sv
module top (
input logic clk_50m, // 50MHz システムクロック
input logic rst_n, // リセットボタン (アクティブLow)
input logic [7:0] leds, // 追加: LED入力信号
output logic hdmi_tx_clk_p, // HDMI TX クロック +
output logic hdmi_tx_clk_n, // HDMI TX クロック -
output logic[2:0] hdmi_tx_p, // データチャネル0 (Blue) +
output logic[2:0] hdmi_tx_n // データチャネル0 (Blue) -
);
logic clk_pix, clk_ser, pll_locked;
logic rst_pix;
logic hsync, vsync, de;
logic [11:0] hcount, vcount;
logic [7:0] r, g, b;
/*
// PLL: 50MHz → 40MHz (ピクセルクロック) / 400MHz (シリアルクロック)
pll u_pll (
.clk_in (clk_50m),
.clk_pix(clk_pix),
.clk_ser(clk_ser),
.locked (pll_locked)
);
*/
Gowin_PLL u_pll(
.clkin(clk_50m), //input clkin
.init_clk(clk_50m), //input init_clk
.clkout0(clk_ser), //output clkout0 371.25MHz
.lock(pll_locked), //output lock
.reset(~rst_n) //input reset
);
Gowin_CLKDIV u_divider(
.clkout(clk_pix), //output clkout 74.25MHz
.hclkin(clk_ser), //input hclkin
.resetn(rst_n) //input resetn
);
// リセット同期化 (ピクセルクロックドメイン)
always_ff @(posedge clk_pix) begin
rst_pix <= ~pll_locked | ~rst_n;
end
// タイミング生成 (800x600@60Hz)
vga_timing u_timing (
.clk (clk_pix),
.rst (rst_pix),
.hsync (hsync),
.vsync (vsync),
.de (de),
.hcount(hcount),
.vcount(vcount)
);
// 7色カラーバー生成
color_bar u_color_bar (
.hcount(hcount),
.r (r),
.g (g),
.b (b)
);
DVI_TX_Top u_dvi_tx(
.I_rst_n(rst_n), //input I_rst_n
.I_serial_clk(clk_ser), //input I_serial_clk
.I_rgb_clk(clk_pix), //input I_rgb_clk
.I_rgb_vs(vsync), //input I_rgb_vs
.I_rgb_hs(hsync), //input I_rgb_hs
.I_rgb_de(de), //input I_rgb_de
.I_rgb_r(r), //input [7:0] I_rgb_r
.I_rgb_g(g), //input [7:0] I_rgb_g
.I_rgb_b(b), //input [7:0] I_rgb_b
.O_tmds_clk_p(hdmi_tx_clk_p), //output O_tmds_clk_p
.O_tmds_clk_n(hdmi_tx_clk_n), //output O_tmds_clk_n
.O_tmds_data_p(hdmi_tx_p), //output [2:0] O_tmds_data_p
.O_tmds_data_n(hdmi_tx_n) //output [2:0] O_tmds_data_n
);
/*
// DVI送信
dvi_tx u_dvi_tx (
.clk_pix (clk_pix),
.clk_ser (clk_ser),
.rst (rst_pix),
.r (r),
.g (g),
.b (b),
.hsync (hsync),
.vsync (vsync),
.de (de),
.tx_clk_p(hdmi_tx_clk_p),
.tx_clk_n(hdmi_tx_clk_n),
.tx_d0_p (hdmi_tx_d0_p),
.tx_d0_n (hdmi_tx_d0_n),
.tx_d1_p (hdmi_tx_d1_p),
.tx_d1_n (hdmi_tx_d1_n),
.tx_d2_p (hdmi_tx_d2_p),
.tx_d2_n (hdmi_tx_d2_n)
);
*/
endmodule・color_bar.sv
// 7色カラーバー生成 (白・黄・シアン・緑・マゼンタ・赤・青)
module color_bar (
input logic [11:0] hcount,
output logic [7:0] r,
output logic [7:0] g,
output logic [7:0] b
);
logic [2:0] bar_index;
// 1280ピクセルを7分割: 183×6 + 182 = 1280
always_comb begin
if (hcount < 183) bar_index = 3'd0; // 白
else if (hcount < 366) bar_index = 3'd1; // 黄
else if (hcount < 549) bar_index = 3'd2; // シアン
else if (hcount < 732) bar_index = 3'd3; // 緑
else if (hcount < 915) bar_index = 3'd4; // マゼンタ
else if (hcount < 1098) bar_index = 3'd5; // 赤
else bar_index = 3'd6; // 青
end
always_comb begin
case (bar_index)
3'd0: begin r = 8'hFF; g = 8'hFF; b = 8'hFF; end // 白
3'd1: begin r = 8'hFF; g = 8'hFF; b = 8'h00; end // 黄
3'd2: begin r = 8'h00; g = 8'hFF; b = 8'hFF; end // シアン
3'd3: begin r = 8'h00; g = 8'hFF; b = 8'h00; end // 緑
3'd4: begin r = 8'hFF; g = 8'h00; b = 8'hFF; end // マゼンタ
3'd5: begin r = 8'hFF; g = 8'h00; b = 8'h00; end // 赤
3'd6: begin r = 8'h00; g = 8'h00; b = 8'hFF; end // 青
default: begin r = 8'h00; g = 8'h00; b = 8'h00; end
endcase
end
endmodule・vga_timing.sv
// 1280x720@60Hz タイミングジェネレータ (74.25MHz)
module vga_timing #(
parameter H_ACTIVE = 1280,
parameter H_FP = 110,
parameter H_SYNC = 40,
parameter H_BP = 220,
parameter V_ACTIVE = 720,
parameter V_FP = 5,
parameter V_SYNC = 5,
parameter V_BP = 20
) (
input logic clk,
input logic rst,
output logic hsync,
output logic vsync,
output logic de,
output logic [11:0] hcount,
output logic [11:0] vcount
);
localparam H_TOTAL = H_ACTIVE + H_FP + H_SYNC + H_BP; // 1056
localparam V_TOTAL = V_ACTIVE + V_FP + V_SYNC + V_BP; // 628
logic [11:0] h_cnt;
logic [11:0] v_cnt;
always_ff @(posedge clk) begin
if (rst) begin
h_cnt <= 12'd0;
v_cnt <= 12'd0;
end else begin
if (h_cnt == H_TOTAL - 1) begin
h_cnt <= 12'd0;
if (v_cnt == V_TOTAL - 1)
v_cnt <= 12'd0;
else
v_cnt <= v_cnt + 1'b1;
end else begin
h_cnt <= h_cnt + 1'b1;
end
end
end
// アクティブLowの同期信号
assign hsync = ~(h_cnt >= H_ACTIVE + H_FP && h_cnt < H_ACTIVE + H_FP + H_SYNC);
assign vsync = ~(v_cnt >= V_ACTIVE + V_FP && v_cnt < V_ACTIVE + V_FP + V_SYNC);
// データ有効期間
assign de = (h_cnt < H_ACTIVE) && (v_cnt < V_ACTIVE);
assign hcount = h_cnt;
assign vcount = v_cnt;
endmodule●実機で動作を確認
▲その1

▲その2
位置x, yと時刻の関数で各ピクセル値を決めるシェーダーでグラデーションカラーを表示させました.