VGA
実機の信号出力はNTSCですが、開発環境にテレビを持ち込むのは面倒で、
画質も劣化します。多くの映像機器で受信可能であることと、
何よりも目の前にディスプレイがあるわけですから、
映像信号はVGAで出力することにしました。
評価ボードにはLVDSポートがついているのでDVIでの出力も可能かもしれませんがこれはおいといて。
VGA信号で使用されるラインは、Red、Green、Blue、水平同期、垂直同期、グランドがあります。
受信側は水平同期信号及び垂直同期信号をトリガとしてRGB信号をディスプレイに表示します。
このときのドット周波数は約25MHzとなっています。
_ ▼ CQSP2E300ボードでの実装
評価ボードではベースクロックを33MHzとしているため、
このまま出力しては横幅が縮まってしまいます。
そこで横幅を3倍にして出力し、ディスプレイの方で横幅を調整してもらいます。
PPUの描画はNTSCに合わせて行われるため、
このままVGAへ出力するにはクロック数が足りません。
このためラインベースのダブルバッファリングを使用し、
バッファをダブルスキャン&フリップすることでPPU描画を間に合わせます。
つまり同じラインを2度出力することになるため、
縦幅が2倍になります。
結果的に、NESの画面領域は横768ドット→縮小されて640ドット、
縦480ドットになりました。

VGAコネクタ回路
方針が決定したのでFPGA内部で水平同期信号、垂直同期信号を生成し、
カラーはRGB各3ビットの信号をそれぞれ出力しました。
またこのままでは不便なのでVGAコネクタ用ボードを製作しました。
といってもDAC用抵抗とD-subコネクタを乗っけただけですが。
ボードによって発色が違うような気がするのは、
抵抗値や配線などの僅かな違いによるものでしょうか。
_ ▼ DE1ボードでの実装
DE1にははじめからVGA出力コネクタが実装されており、
クロックも50MHzから25MHzを生成できるのでちょうど水平2倍で出力しました。
垂直方向はやはりダブルバッファリングが必要なためこれも2倍です。
ただし、実質的に上下8ドットは表示されないことが多いため、
NESの画面領域は横512ドット、縦448ドットになりました。
ということで vga_ctrl.sflp と VGA_test.sflp
%i "vga_ctrl.h"
circuit VGA_test
{
VGA_ctrl vga;
output VGA_HS, VGA_VS;
output VGA_R<4>, VGA_G<4>, VGA_B<4>;
reg_wr red_out_reg<4>, grn_out_reg<4>, blu_out_reg<4>;
reg_wr clock25M_div;
Display dis; // ラインバッファ
reg_ws dis_timing;
stage_name clock25M { task do(); }
par{
vga.vt_su = 0b0000000001; // 1
vga.vt_vu = 0b0000011111; // 31
vga.vt_nu = 0b0000110001; // 49
vga.vt_nd = 0b0111110001; // 497 (nu + 224*2)
vga.vt_vd = 0b0111111111; // 511 (vu + 480)
vga.vt_sd = 0b1000001011; // 524 -1
vga.ht_su = 0b0001011100; // 92
vga.ht_vu = 0b0010010111; // 151
vga.ht_nu = 0b0010111100; // 190 -2
vga.ht_nd = 0b1010111100; // 702 -2 (nu + 512)
vga.ht_vd = 0b1011100101; // 741 (vu + 640 -a)
vga.ht_sd = 0b1100011010; // 795 -1
if(vga.win_valid){
red_out_reg := dis.r_out || 0b0;
grn_out_reg := dis.g_out || 0b0;
blu_out_reg := dis.b_out || 0b0;
}
else{
// NES画面外の色(ディスプレイ自動幅調整のため)
red_out_reg := 0b0010;
grn_out_reg := 0b0010;
blu_out_reg := 0b0010;
}
VGA_HS = vga.hsync;
VGA_VS = vga.vsync;
VGA_R = red_out_reg & (4#vga.view_valid);
VGA_G = grn_out_reg & (4#vga.view_valid);
VGA_B = blu_out_reg & (4#vga.view_valid);
}
instruct vga.dis par{
if(dis_timing) dis.read();
dis_timing := ^dis_timing;
}
instruct vga.nes_hsync par{
dis.bank_change();
generate nes_line.do();
}
instruct vga.nes_vsync par{
pad.key_get();
}
stage clock25M {
par{
clock25M_div := ^clock25M_div;
if(clock25M_div) vga.run();
}
}
}
Copyright(C) pgate1 All Rights Reserved.
|