かずの不定期便ブログ

備忘録代わりに書きます

FPGA Sipeed Tang Nano 9Kを使ってHDMI表示(720p)

リーズナブルFPGAシリーズのTang Nano 9Kを使ってHDMI表示させました

まだ、wikiではTangNano 9KではHDMI表示サンプルがなかったのでTangNano 4K版を参考に作成しました。
wikiはこちら
wiki.sipeed.com


開発環境(GOWIN IDE)の準備

ググるとTangNanoのIDEインストールについてたくさんヒットしますが、情報が古く真似て設定してもラインセンスがつかめなくて動作しないです。
Education版を使うとライセンスが不要で使えます。少し前のverですと使えるデバイスがTangNano 4Kのみだったようですが、現在の版は他のデバイスも使える様です(2022/8/23現在ダウンロードできる版数はV1.9.8.07)。Tang Nano 9Kももちろんいけます。

他には、ノードロックライセンスを発行する手がありますが、MACアドレスと紐づくし、一年で切れるようなので、避けてました。


※蛇足ですが、多くのサイトに掲載されているSipeedのフローティングライセンスサーバーを見るという方法は、最近のGOWIN IDEではライセンス形態が変更になったため、Sipeedのライセンスは取りにいけないようです。(僕はライセンスサーバーが落ちているのかと勘違いしていました...)
インストールフォルダにあるドキュメント関連が収められているフォルダ(C:\Gowin\Gowin_V1.9.8.xx\IDE\doc\JPN)にある"RN791-1.0J_Gowinソフトウェアのライセンス更新 リリースノート.pdf"にセキュリティ強化のため、Gowin ソフトウェア V1.9.8.01(2021 年 11 月 2 日にリリース)以降から変更したと記載があります。

Education版のダウンロード

t.co

ここからGowin EDA Student edition の Windows download ボタンを押してダウンロードし、ZIPを解凍してexeファイルを実行すればインストールされます。特に迷うようなところはないかと思います。

サンプルを実行してみる

いきなり本題のHDMI表示をやってもいいのですが、、、実は、先のEducation版、デバイスを見つけられずFPGAへの書き込みが出来ません。なので、その問題解決を(動くはずであろう)サンプルでやってしまうのが良いです。(むぅ、中々一筋縄ではいかないです...)

以下のサイト(先ほどのwiki)にTang Nano 9Kのサンプルコードが置いてあるので、Blinkというコードを実行してみます。
Tang Serial Development Board - Sipeed Wiki
左のwindowで選択できるTang Nano Example →2.4. Tang Nano 9K→Blinkをクリックします。 ここに記載の通りに作業を行えばよいです。スクリーンショットと共に説明されているので分かりやすいです。
ただ一点、Use DONE as regular IOにチェックを入れるところがすぐに分からなかったので補足すると、メニューのProjectをクリック→Configurationを選択
ここで現れたwindowのPlace&RouteからDual-purpose pin を選択すれば
□Use DONE as regular IOが現れます。
3. Systhesize, constrain, place&route に記載があるwindowですが、Processペインを選択すると出てきます。SysthesizeをダブルクリックもしくはSynthesize→右クリックで合成できます。え?もう終わったの?って感じで一瞬で終了します。

FloorPlanerでは、I/O constraintsペインを選択して各ポートとIOを紐づけます。
サンプル説明通りLocation列のところにピン番号 led[0]-[5] = 10,11,13-16、sys_clk=52pin, sys_rst_n=4を入力すればOKです。
sys_clkのみがIO TypeがLVCMOS33になる事に気を付けてsaveしてwindowを閉じます。
そしてPlace&Rootをダブルクリックでビットストリームを作成します(これもすぐに終わります。快適です)

バイスがPCから見えず、FPGAに書き込めない事案発生

さて、問題は次の工程でして、デバイスにbit streamを書き込む説明の章
4. Download to device
が上手くいきません。
PCとUSBケーブルでデバイスをつなげた上で、Program Deviceをダブルクリックするのですが、写真の様なエラーになってデバイスを見つけてくれません。

NO USB Cable

以下のFAQを見ます。
Note - Sipeed Wiki
ここに記載の
Make sure there are 2 converters in Device Manager before reading.
を確認します。記載の通りデバイスマネージャーにはUSB Serial Converter A, USB Serial Converter Bの両方が見えてます。ということでこちらはOK

PCからFPGAが見えない問題の解決

次に
1.2. Trouble downloading#
を確認。指示通り"programmer"を指示されたサイトからダウンロードして代わりにコレを使えとあります。
https://dl.sipeed.com/shareURL/TANG/programmer

  • Downloadする際のCaptcha が難易度高い。この手のはいつも苦労する。
  • Refresh ボタンを押して分かりやすいものが出てくるまで何度かトライ
  • ダウンロード出来たら
  • Programmer.7z を解凍します
  • Programmerフォルダが出来るので、その中のbinフォルダにある"programmer.exe"を実行します

windowの名前が"Programmer 2 "に変化しています。Edit→Add deviceで

Series GW1NR
Device GW1NR-9C
Operation SRAM Program
FSfile 先ほどIDEでPlace&routeで生成された"projectフォルダ\impl\pnr\プロジェクト名.fs" を選択します

メニューのEdit→Program/configure を選択すればSRAMへ書き込んでくれて、FPGAが動き出します。

programmerの差し替え

無事動いたのを確認した後、せっかくなんで、GOWINのIDEからProgram Deviceとした際に今回ダウンロードしたprogramのバイナリが走るようにします。
先ほど動かしたprogrammer2とIDEを終了させておきます。IDEのインストールフォルダを見ると、僕の場合は
C:\Gowin\Gowin_V1.9.8.07_Education
です。
ここにProgrammerというフォルダが見えますので、このフォルダの名前を変更します。僕はprogrammer_orgへ変更しました。
その後、先ほど別途ダウンロードしたProgrammer.7zを解凍して出来たProgrammerフォルダごと、ここへ移動させます。下記の様な状態になりました。

programmerの差し替え

この状態で、再度IDEを起動させて、sampleで動かしたLEDプロジェクトを開きProcessタブの"Program Device"をクリックします。今度はきちんとデバイスが認識されて書き込みが成功し動きます。

HDMI表示用の回路を作成

まずは、Tang Nano 4KではHDMI表示用サンプルがあるので、これをベースに修正します。
先ほど、紹介したwikiのexampleから
Display camera content by HDMI project Repository:
github.com
ここにある"Nano4K_HDMI"プロジェクトを参考にします。
参考にするのはtop.vのみです。他にdvi_txというフォルダも必要そうに見えますが、ここはIP core generatorでdvi_txのIPをインスタンスすると生成されるフォルダになるので不要です。

プロジェクトの作成

IDEのメニューでFile→New→FPGA Design Projectで新規にプロジェクトを作成します。僕はプロジェクト名をNano9K_HDMIとしました。
Select DeviceではSeries=GW1NRを選択すればPartは1種類しか出てきません、これがNano9Kです。
プロジェクトを選択し(フォルダーのようなアイコン)
File→New→Verilog Fileでtop.vを作ります(.vは記載不要。入れるとファイル名がtop.v.vになってしまいます)。空のファイルが開くので先ほどのwikiのNano4K用のtop.vの中身をコピペします。
ソースの中身を確認します。

  • 1440p用(2560x1440ピクセル)のlocalparam設定がある
  • PLLVRがPLLのインスタンス
  • CLKDIVは上記の出力を分周してピクセルクロックclk_pixelを作っている
  • DVI_TX_TopにてHDMI用の信号を作成していそう
  • cnt_h, cnt_vでピクセル位置を数えている
  • 上記カウンタから同期信号rgb_hs, rgb_vsを作成
  • 表示データ有効期間を表すrgb_de信号を作成
  • 表示データは一見だと分かりません。動けば分かる事なので、あまりここでは気にしない

というような事が分かります。
DVI_TX_TopがIPで先ほどのwikiにあったdvi_txフォルダのものです。
この状態で合成すると(ProcessタブのSynthesizeのダブルクリック)
ERROR (EX3937) : Instantiating unknown module 'DVI_TX_Top'("x:xxxx\src\top.v":126)
とエラー終了します。さきほどコピーしてこなかったdvi_txというフォルダをコピーすれば、ここでのエラーは出なくなるのですが、nano4kとnano9kの違いから後の工程でやはりエラーが出てしまいます。

DVI_TX_Topのインスタンス

これはIPなのでIP Core Generatorで作ります。下の絵のボタンを押してください。

IP Core Generator

IPの選択画面が出てくるのでMultimedia→DVI TXを選択し、ダブルクリックしてください。

  • Using External clockのチェックは外す。これでIP側でserialクロックを生成してくれます。IPに対するクロックはピクセルクロックの入力のみでOKになります。(wikiのはインスタンス部の端子情報を見るとここはONにしているようです)
  • TX Clock Frequencyは74.25MHzを入れてます(これは720p用のピクセルクロックです)
  • IO setting:ELVDS(TLVDSだとIOとの紐づけが上手くいかなかった。IO電圧の問題?)
  • Disable I/O isertionのチェックは外す

OKを押します。
Do you want to add generated files to current project?と聞かれるのでOKを押します。
TX Clock Frequencyは80MHzが最大です。つまりピクセルクロックが80MHz以下の解像度になります。
16:9の画角だと1280x720(720p)が最大になります。

DVI TX IP設定

このあたりの仕様は以下からダウンロードできます。サインインが必要なのでアカウント作成が必須です。日本語サイトがあるのが素晴らしいです。
IPとリファレンスデザイン→Gowin DVI TX RX→Gowin DVI TX RX IPユーザーガイドをダウンロード

www.gowinsemi.com

また,top.vでの本IPのインスタンスにはI_serial_clk端子が存在しているので、//でコメントアウトします。先ほどのIP generateでUsing External clockのチェックを入れれば差分はなくなりますが、シリアルクロックの作成が別途必要になるので、取っちゃいます。

再度、合成します。
ERROR (EX0312) : There is no PLLVR resource in current device
この様なエラーが出ます。これはnano9KではPLLVRという名前のPLLは無いというメッセージです。nano9Kで使えるPLLはrPLLという名前のPLLです。
先ほどのGOWINのサイトでUG286で検索すると
Gowin Clock ユーザーマニュアル
がヒットします。5.1章と5.2章にそれぞれrPLL,PLLVRの対応デバイスの記載があり、nano9kがrPLLに対応している事が分かります。
それぞれ端子一覧を見ると端子差分はVREN入力がないくらいです。同名の端子に機能の違いは無いようです。VRENを//コメントアウトします。またモジュール名をPLLVR→rPLLへ変更します。
パラメータのDEVICEがGW1NSR-4Cになっているのでnano9kのデバイス名"GW1NR-9C"へ変更します。

再度、合成します。
ERRORは消えました。しかしPLL関連でWARNが出ています。
WARN (EX0210) : Invalid VCO frequency "(FCLKIN*(FBDIV_SEL+1)*ODIV_SEL)/(IDIV_SEL+1)" to instance "pll", suitable range is from 400MHz to 1200MHz
WARN (EX0210) : Invalid CLKOUT frequency "FCLKIN*(FBDIV_SEL+1)/(IDIV_SEL+1)" to instance "pll", suitable range is from 3.125MHz to 600MHz

元にしたnano4kのPLLの設定が、仕様範囲外になっている為です。
#同じ仕様制限はnano4kもあるはずなのですが。。。sampleは動くのだろうか?謎です。
いずれにしても、wikiのは1440pの設定ですので、見直します。
先にも記載したようにピクセルクロックが80MHz以下でしか扱えないので、解像度は以下とします。

  • 1440x720 pixel(いわゆる720p)
  • 720pのピクセルクロックは74.25MHz

幸い720pのlocalparamの設定はオリジナルで//コメントで残っていますので、1440p側を消して720p側を有効にします。

PLLの設定

PLLの設定は、wikiのtop.vの様にPLLの設定をlocalparamで設定する方法でもよいですが、先ほどのDVI_TxのIP呼び出しの様にrPLLも同様にIP呼び出しすれば、入力クロックと、出力クロックの設定を行うだけで、パラメータは自動計算してくれます。

CLKDIVを2分周で利用するとすれば、74.25MHz x 2 = 148.5MHzをPLLで生成すればよいので、以下の図のようにしてrPLLの設定をして呼び出します。
入力クロック:27MHz
出力クロック:148.5MHz
EnableL Lockのチェックを忘れずに。

rPLLの設定

gowin_rpll_tmp.vが開きます。これはIPのインスタンス例です。これをcopy&pasteしてtop.vに貼り付けます。インスタンス名と、接続wire名を合わせて変更します。
また、PLLを直で呼び出している記述は削除します。
CLKDIVは忘れずに2分周へ変更します。

    Gowin_rPLL pll_wrap(
        .clkout(clk_serial), //output clkout
        .lock(pll_locked), //output lock
        .clkin(clk) //input clkin
    );
CLKDIV #(
    //.DIV_MODE (5)
    .DIV_MODE (2)   // 720p
) clk_pixel_gen (
    .HCLKIN (clk_serial),
    .RESETN (rst_n),
    .CALIB  (1'b0),
    .CLKOUT (clk_pixel)
);

再合成を行います。今度は特にWarningは出ません。

IOポートの設定

User ConstrintsのFloorPlanerをダブルクリックします。初回はcstファイルがないと言われますがOKで続行します。
I/O Constraintsのタブで入出力ポートのIOピン番号の紐づけを行います。
LEDのサンプル同様にclk端子は52ピンのLVCMOS33、rst_n端子は4ピンのLVCMOS18。
HDMI系の端子ですが、LEDのサンプルでも紹介されていたTang Nano 9Kの回路図で、どのピンがHDMIに割り付けられているか確認します。
https://dl.sipeed.com/shareURL/TANG/Nano%209K/2_Schematic
HDMI関連はBANK1の右上の方に記載があります。差動信号(pとnがある)であり、68-75ピンであることが分かります。
例えばクロック出力だと、I/O Constraintsにはhdmi_clk_p行にDiff_Pairとしてhdmi_clk_nが提示されています。これに対応するピン番号68と69を,(カンマ区切り)でLocationへ設定します。

差動信号の入力方法

同様に他の端子も設定し、saveボタンを押した後終了します。

他のHDMI端子の設定

Place&Route

Place&Routeボタンをダブルクリックしてビットストリームを作成します。

Program Device

Program Deviceボタンをダブルクリックして、立ち上がったProgrammer2で
Edit→Program/Configure

結果

HDMI出力をモニタへ繋ぎます。
白い線が、左上から徐々に伸びていき、1ライン終了すると次のラインへ移動して、線を伸ばしていくような動画です。
この結果から踏まえて、もう一度top.vの
// Video Pattern Generator
以降を読むと、1frame分のクロックを数えると、隣のpixelを黒から白に変更。水平画素数分が終わると、次ラインの左端のpixから白へ変更するというパターンジェネレータの様です。
また、モニタの映像情報を表示させたところ

  • 1280x720
  • fH 45.0kHz
  • fV: 60.0 Hz
  • fD: 74.2MHz

と表示されました。解像度、フレームレート共に予定通りの数字になっていますのでOKです。

ソースを提示します。

Video Pattern Generatorの部分をFPGAプログラム大全から借用したグラデーション表示へ変更しました。
cnt_de、同期信号についてはwikiのものを流用しています。

また、内蔵OSC(Nano9Kは125MHz)を使って1sec毎にLチカさせるコードを仕込みました。
内蔵OSCはIP呼び出しで使えます。IP Core generatorでHard Module→CLOCK→OSCでインスタンス出来ます。1/4分周で利用しました。

IPのOSCをインスタンス

内蔵OSCのみで今後回路を作成する場合の参考になればと思います。
led出力ポートを作成したので、Floor Plannerでled端子へピン10を割り当ててます。
実行時に1sec毎に点滅するので動作確認がLEDの点滅で可能です。

以下にこれらを加えたコードを示します。

module top (
    input  wire       rst_n,
    input  wire       clk,
    output reg        led,
    output wire       hdmi_clk_p,
    output wire       hdmi_clk_n,
    output wire [2:0] hdmi_data_p,
    output wire [2:0] hdmi_data_n
);

// f_CLKOUT = f_CLKIN * FBDIV / IDIV, 3.125~600MHz
// f_VCO = f_CLKOUT * ODIV, 400~1200MHz
// f_PFD = f_CLKIN / IDIV = f_CLKOUT / FBDIV, 3~400MHz

//localparam PLL_IDIV  =  2 - 1; // 0~63
//localparam PLL_FBDIV = 57 - 1; // 0~63
//localparam PLL_ODIV  =      2; // 2, 4, 8, 16, 32, 48, 64, 80, 96, 112, 128

localparam PLL_IDIV  =  6 - 1; // 0~63
localparam PLL_FBDIV = 33 - 1; // 0~63 
localparam PLL_ODIV  =      4; // 2, 4, 8, 16, 32, 48, 64, 80, 96, 112, 128


// 720p
localparam DVI_H_BPORCH = 12'd220;
localparam DVI_H_ACTIVE = 12'd1280;
localparam DVI_H_FPORCH = 12'd110;
localparam DVI_H_SYNC   = 12'd40;
localparam DVI_H_POLAR  = 1'b1;
localparam DVI_V_BPORCH = 12'd20;
localparam DVI_V_ACTIVE = 12'd720;
localparam DVI_V_FPORCH = 12'd5;
localparam DVI_V_SYNC   = 12'd5;
localparam DVI_V_POLAR  = 1'b1;

// 1080p
//localparam DVI_H_BPORCH = 12'd148;
//localparam DVI_H_ACTIVE = 12'd1920;
//localparam DVI_H_FPORCH = 12'd88;
//localparam DVI_H_SYNC   = 12'd44;
//localparam DVI_H_POLAR  = 1'b1;
//localparam DVI_V_BPORCH = 12'd36;
//localparam DVI_V_ACTIVE = 12'd1080;
//localparam DVI_V_FPORCH = 12'd4;
//localparam DVI_V_SYNC   = 12'd5;
//localparam DVI_V_POLAR  = 1'b1;

/*
// 1440p
localparam DVI_H_BPORCH = 12'd40;
localparam DVI_H_ACTIVE = 12'd2560;
localparam DVI_H_FPORCH = 12'd8;
localparam DVI_H_SYNC   = 12'd32;
localparam DVI_H_POLAR  = 1'b1;
localparam DVI_V_BPORCH = 12'd6;
localparam DVI_V_ACTIVE = 12'd1440;
localparam DVI_V_FPORCH = 12'd13;
localparam DVI_V_SYNC   = 12'd8;
localparam DVI_V_POLAR  = 1'b0;
//localparam DVI_H_BPORCH = 12'd80;
//localparam DVI_H_ACTIVE = 12'd2560;
//localparam DVI_H_FPORCH = 12'd48;
//localparam DVI_H_SYNC   = 12'd32;
//localparam DVI_H_POLAR  = 1'b1;
//localparam DVI_V_BPORCH = 12'd33;
//localparam DVI_V_ACTIVE = 12'd1440;
//localparam DVI_V_FPORCH = 12'd3;
//localparam DVI_V_SYNC   = 12'd5;
//localparam DVI_V_POLAR  = 1'b0;
*/

wire rst;
wire clk_serial;
wire clk_pixel;
wire pll_locked;

reg       rgb_vs;
reg       rgb_hs;
reg       rgb_de;
reg [7:0] rgb_r;
reg [7:0] rgb_g;
reg [7:0] rgb_b;

assign rst = ~rst_n;

/*
//PLLVR #(
rPLL #(
    .FCLKIN    (27),
    .IDIV_SEL  (PLL_IDIV),
    .FBDIV_SEL (PLL_FBDIV),
    .ODIV_SEL  (PLL_ODIV),
//    .DEVICE    ("GW1NSR-4C")
    .DEVICE    ("GW1NR-9C")
) pll (
    .CLKIN    (clk),
    .CLKFB    (1'b0),
    .RESET    (rst),
    .RESET_P  (1'b0),
    .FBDSEL   (6'b0),
    .IDSEL    (6'b0),
    .ODSEL    (6'b0),
    .DUTYDA   (4'b0),
    .PSDA     (4'b0),
    .FDLY     (4'b0),
    //.VREN     (1'b1),
    .CLKOUT   (clk_serial),
    .LOCK     (pll_locked),
    .CLKOUTP  (),
    .CLKOUTD  (),
    .CLKOUTD3 ()
);
*/
    Gowin_rPLL pll_wrap(
        .clkout(clk_serial), //output clkout
        .lock(pll_locked), //output lock
        .clkin(clk) //input clkin
    );

CLKDIV #(
    //.DIV_MODE (5)
    .DIV_MODE (2)   // 720p
) clk_pixel_gen (
    .HCLKIN (clk_serial),
    .RESETN (rst_n),
    .CALIB  (1'b0),
    .CLKOUT (clk_pixel)
);

DVI_TX_Top dvi_tx (
    .I_rst_n       (rst_n),       // input I_rst_n
    //.I_serial_clk  (clk_serial),  // input I_serial_clk
    .I_rgb_clk     (clk_pixel),   // input I_rgb_clk
    .I_rgb_vs      (rgb_vs),      // input I_rgb_vs
    .I_rgb_hs      (rgb_hs),      // input I_rgb_hs
    .I_rgb_de      (rgb_de),      // input I_rgb_de
    .I_rgb_r       (rgb_r),       // input [7:0] I_rgb_r
    .I_rgb_g       (rgb_g),       // input [7:0] I_rgb_g
    .I_rgb_b       (rgb_b),       // input [7:0] I_rgb_b
    .O_tmds_clk_p  (hdmi_clk_p),  // output O_tmds_clk_p
    .O_tmds_clk_n  (hdmi_clk_n),  // output O_tmds_clk_n
    .O_tmds_data_p (hdmi_data_p), // output [2:0] O_tmds_data_p
    .O_tmds_data_n (hdmi_data_n)  // output [2:0] O_tmds_data_n
);

// Video Timing Generator

reg [11:0] cnt_h;
reg [11:0] cnt_h_next;
reg [11:0] cnt_v;
reg [11:0] cnt_v_next;

always @(negedge rst_n or posedge clk_pixel)
    if (!rst_n) begin
        cnt_h <= 12'd0;
        cnt_v <= 12'd0;
    end else if (!pll_locked) begin
        cnt_h <= 12'd0;
        cnt_v <= 12'd0;
    end else begin
        cnt_h <= cnt_h_next;
        cnt_v <= cnt_v_next;
    end

always @(*) begin
    if (cnt_h == DVI_H_BPORCH + DVI_H_ACTIVE + DVI_H_FPORCH + DVI_H_SYNC - 1'd1) begin
        cnt_h_next = 12'd0;
        if (cnt_v == DVI_V_BPORCH + DVI_V_ACTIVE + DVI_V_FPORCH + DVI_V_SYNC - 1'd1) begin
            cnt_v_next = 12'd0;
        end else begin
            cnt_v_next = cnt_v + 1'd1;
        end
    end else begin
        cnt_h_next = cnt_h + 1'd1;
        cnt_v_next = cnt_v;
    end
end

always @(*) begin
    if (cnt_h < DVI_H_BPORCH + DVI_H_ACTIVE + DVI_H_FPORCH) begin
        rgb_hs = ~DVI_H_POLAR;
    end else begin
        rgb_hs = DVI_H_POLAR;
    end

    if (cnt_v < DVI_V_BPORCH + DVI_V_ACTIVE + DVI_V_FPORCH) begin
        rgb_vs = ~DVI_V_POLAR;
    end else begin
        rgb_vs = DVI_V_POLAR;
    end

    if (cnt_h < DVI_H_BPORCH || cnt_h >= DVI_H_BPORCH + DVI_H_ACTIVE) begin
        rgb_de = 1'b0;
    end else if (cnt_v < DVI_V_BPORCH || cnt_v >= DVI_V_BPORCH + DVI_V_ACTIVE) begin
        rgb_de = 1'b0;
    end else begin
        rgb_de = 1'b1;
    end
end

// Video Pattern Generator
/*
reg [23:0] cnt_white;
reg [23:0] cnt_white_next;

always @(negedge rst_n or posedge clk_pixel)
    if (!rst_n) begin
        cnt_white <= 24'd0;
    end else if (cnt_h == 12'd0 && cnt_v == 12'd0) begin
        cnt_white <= cnt_white_next;
    end

always @(*)
    if (cnt_white == DVI_H_ACTIVE * DVI_V_ACTIVE - 1'd1) begin
        cnt_white_next = 24'd0;
    end else begin
        cnt_white_next = cnt_white + 1'd1;
    end
*/

reg [23:0] cnt_de;
reg [23:0] cnt_de_next;

always @(negedge rst_n or posedge clk_pixel)
    if (!rst_n) begin
        cnt_de <= 1'd0;
    end else begin
        cnt_de <= cnt_de_next;
    end

always @(*)
    if (rgb_vs == DVI_V_POLAR) begin
        cnt_de_next = 24'd0;
    end else if (rgb_de) begin
        cnt_de_next = cnt_de + 1'd1;
    end else begin
        cnt_de_next = cnt_de;
    end

/*
wire [23:0] rgb_next;

always @(negedge rst_n or posedge clk_pixel)
    if (!rst_n) begin
        {rgb_r, rgb_g, rgb_b} <= 24'h000000;
    end else begin
        {rgb_r, rgb_g, rgb_b} <= rgb_next;
    end

assign rgb_next = cnt_de < cnt_white ? 24'hFFFFFF : 24'h000000;
*/

reg [23:0] FRAME_LOOP;
always @(negedge rst_n or posedge clk_pixel)
    if(!rst_n)
        FRAME_LOOP <= 24'h00_0000;
    else if(rgb_de) begin
        if(FRAME_LOOP==(DVI_H_ACTIVE*DVI_V_ACTIVE-1))
            FRAME_LOOP <= 24'h00_0000;
        else
            FRAME_LOOP <= FRAME_LOOP +1;
    end

always @(negedge rst_n or posedge clk_pixel)
    if (!rst_n) begin
        {rgb_r, rgb_g, rgb_b} <= 24'h000000;
    end
    else begin
        {rgb_r, rgb_g, rgb_b} <= FRAME_LOOP;
    end

// 1secに一回LEDを付けたり消したり
wire osc_62p5M; // 62.5MHz:  OSC設定=1/4
    Gowin_OSC OSCDIV2 (
        .oscout(osc_62p5M) //output oscout
    );

reg[27:0] sec_cnt;
wire cnt_1sec = (sec_cnt == (28'd62_500_000/2-1));

always @(negedge rst_n or posedge osc_62p5M)
    if(!rst_n)
        sec_cnt <= 28'h000_0000;
    else if(cnt_1sec)
        sec_cnt <= 28'h000_0000;
    else
        sec_cnt <= sec_cnt + 28'h000_0001;

always @(negedge rst_n or posedge osc_62p5M)
    if(!rst_n)
        led <= 1'b0;
    else if(cnt_1sec)
        led <= ~led;

endmodule

モニタ表示の様子

モニタに映しだされたグラデーション画像を写真に撮りました。肉眼ではもっときれいに見えるのですが、、、ノイズがあるように映ってしまいます。

モニタへ映し出した様子

まとめ

  • 価格がリーズナブルです
  • 日本語マニュアルがあるのがありがたいです。この部分は驚きました。
  • wikiは分かりやすいです。ただsampleについてはまだ準備中の箇所があります(nano9kのHDMIも準備中)。ボードに実装されているデバイスについて一通りサンプルがあるとありがたいかなと思いました。
  • IDEは、インストールサイズが小さく、なによりキビキビ動作します。合成速度も速いです。今回は回路規模が小さいという事もありますが、これだけの速度があるなら、規模が大きくなっても苦になる事は無さそうです。
  • IPのインスタンスする際にドキュメントへのリンクが出てきたりで親切だと感じました
  • 論理simulationは使用者側がなんとかsimulatorを用意しないといけません。今回はsimulationをやってません。フリーで使えるsimulatorはiverilogがあるので、ホビー向けには、これで行うのが良いのかなと思います。DVI_TXのリファレンスデザインのsimulationフォルダを見るとvcsかmodelsimが前提の様でした。

参考にさせていただいた書籍およびサイト

Tang Serial Development Board - Sipeed Wiki
IPとリファレンスデザイン | Gowin
FPGAプログラミング大全 Xilinx編 第2版 - 秀和システム あなたの学びをサポート! (僕が参考にしたのは1版ですが)
UG286-1.9.5J_Gowin Clockユーザーガイド.pdf
Tang_Nano_9K_3672_schematic.pdf
DS117-2.9.3E_GW1NR series of FPGA Products DataSheet.pdf


以上になります。
次はTangNano9Kに実装されてるSDカードスロットを使いたいなぁ。いつになる事やら