モノ創りで国造りを

ハード/ソフト問わず知見をまとめてます

FPGAでVGA出力

背景

最近verilogを用いた画像の描画について勉強したので、IntelのSoc FPGA基板、DE10-Nanoを使ってVGA経由でディスプレイに画像を出力しようと思う。

VGA

VGAはR、G、Bのデータをアナログ出力する。
hsyncでスキャンラインを一ピクセル下に移動させ、一画面表示し終えたらvsyncで画面の左上に戻る。

画像サイズの設定

ディスプレイによって対応可能なVGAの画面サイズとリフレッシュレートが異なる。以下の数値が標準。
f:id:yuji2yuji:20190820164252p:plain

今回は640x480、60Hzで画像を表示する。
使用するクロックは25MHzとする。

ソース

DE1-Nanoのクロックは50MHz、HPS用に25MHzもFPGAに入力されているが、
このクロックはFPGAのクロックには使えなかった。
そのため50MHzを2分周して25MHzを生成した。
リセット信号は基板上のスイッチを使用。

`include "hvsync_generator.v"

/*
A simple test pattern using the hvsync_generator module.
*/

module vga_top(clk, reset, hsync, vsync, rgb);

  input clk, reset;
  output hsync, vsync;
  output [2:0] rgb;
  wire display_on;
  wire [9:0] hpos;
  wire [9:0] vpos;
  
  reg clk_divider2;

  hvsync_generator hvsync_gen(
    .clk(clk_divider2),
    .reset(reset),
    .hsync(hsync),
    .vsync(vsync),
    .display_on(display_on),
    .hpos(hpos),
    .vpos(vpos)
  );

  wire r = display_on && (((hpos&4'b1111)==0) || ((vpos&4'b1111)==0));
  wire g = display_on && vpos[5];
  wire b = display_on && hpos[5];
  assign rgb = {b,g,r};
  
  always @(posedge clk)begin
    clk_divider2 <= ~clk_divider2;
  end
  
endmodule
`ifndef HVSYNC_GENERATOR_H
`define HVSYNC_GENERATOR_H

/*
Video sync generator, used to drive a simulated CRT.
To use:
- Wire the hsync and vsync signals to top level outputs
- Add a 3-bit (or more) "rgb" output to the top level
*/

module hvsync_generator(clk, reset, hsync, vsync, display_on, hpos, vpos);

  input clk;
  input reset;
  output reg hsync, vsync;
  output display_on;
  output reg [9:0] hpos;
  output reg [9:0] vpos;

  // declarations for TV-simulator sync parameters
  // horizontal constants
  parameter H_DISPLAY       = 640; // horizontal display width
  parameter H_BACK          =  48; // horizontal left border (back porch)
  parameter H_FRONT         =  16; // horizontal right border (front porch)
  parameter H_SYNC          =  96; // horizontal sync width
  // vertical constants
  parameter V_DISPLAY       = 480; // vertical display height
  parameter V_TOP           =  31; // vertical top border
  parameter V_BOTTOM        =  11; // vertical bottom border
  parameter V_SYNC          =   2; // vertical sync # lines
  // derived constants
  parameter H_SYNC_START    = H_DISPLAY + H_FRONT;
  parameter H_SYNC_END      = H_DISPLAY + H_FRONT + H_SYNC - 1;
  parameter H_MAX           = H_DISPLAY + H_BACK + H_FRONT + H_SYNC - 1;
  parameter V_SYNC_START    = V_DISPLAY + V_BOTTOM;
  parameter V_SYNC_END      = V_DISPLAY + V_BOTTOM + V_SYNC - 1;
  parameter V_MAX           = V_DISPLAY + V_TOP + V_BOTTOM + V_SYNC - 1;

  wire hmaxxed = (hpos == H_MAX) || !reset;  // set when hpos is maximum
  wire vmaxxed = (vpos == V_MAX) || !reset;  // set when vpos is maximum
  
  // horizontal position counter
  always @(posedge clk)
  begin
    hsync <= (hpos>=H_SYNC_START && hpos<=H_SYNC_END);
    if(hmaxxed)
      hpos <= 0;
    else
      hpos <= hpos + 1;
  end

  // vertical position counter
  always @(posedge clk)
  begin
    vsync <= (vpos>=V_SYNC_START && vpos<=V_SYNC_END);
    if(hmaxxed)
      if (vmaxxed)
        vpos <= 0;
      else
        vpos <= vpos + 1;
  end
  
  // display_on is set when beam is in "safe" visible frame
  assign display_on = (hpos<H_DISPLAY) && (vpos<V_DISPLAY);

endmodule

`endif

Pin PlannerでFPGAのピンアサインを設定する。
f:id:yuji2yuji:20190821164227p:plain

セットアップ

DE10-NanoにはVGA端子がないのでVGAケーブルをばらしてGPIOに接続できるようにする。

VGAコネクタのソケットのピン番号と各ピンの用途は以下の通り。
f:id:yuji2yuji:20190821143929p:plain
f:id:yuji2yuji:20190821143854p:plain

RED_RTN、GREEN_RTN、BLUE_RTNは全てGNDに接続する。
SDAとSCLは未接続でよい。
VGAケーブルをバッサリカットし、Red,Green,Blue,Vsync,Hsync,GNDの合計6本を2.54mmピッチの汎用コネクタを半田付けする。
半田付けする際に、R/G/Bのケーブルには直列に300Ωの抵抗を挿入した。
なぜなら、VGAは規格として信号電圧が最大0.7Vであり、ディスプレイ側で75Ωの抵抗がプルダウンされているため、
DE10-Nanoの出力電圧を3.3Vに設定すると、R/G/Bの信号電圧は300Ωと75Ωで分圧されて0.66Vとなるからである。

わかりにくいけど、DE10-Nanoにケーブルを取り付けた写真。
f:id:yuji2yuji:20190821143101j:plain

*このVGAケーブルはNCの9ピンに線が接続されていた。何故??

結果

映像がでた。
f:id:yuji2yuji:20190821142948j:plain
赤と青のピンアサインを間違えて、伊勢丹みたいになってしまった。まぁいいか。

最初はHsyncとVsyncで勝手に表示画像サイズが決まると勘違いし、画像サイズを適当な値にしていたら、ディスプレイに「Out of Range」と表示されて困った。

その後、画素数とクロックを変更することで事なきを得た。

ここ数週間、色々とFPGAの勉強をしていたけど、こういったアウトプットがあるとモチベーションが高まる。

実践は座学の何倍も価値があるというのを改めて実感。