概要
FPGAを使用した、HDMI画像表示回路を作成方法についてまとめました。
動作仕様&使用機材
動作仕様
項目 | 仕様 |
---|---|
表示サイズ | Full-HD(1920x1080) |
捜査 | プログレッシブ |
フレームレート | 60fps |
画像処理クロック | 148.5MHz |
使用機材
項目 | 機材 |
---|---|
FPGA | Spartan6(XC6SLX45-3CSG324) |
FPGAボード | ATLYS (DIGILENT社製) |
回路ブロック
ブロック | Name | 概要 |
---|---|---|
同期信号生成部 | IMG_SYNC_GEN | 同期信号を生成します |
画像重畳部 | IMG_BRENDER | 同期信号に画像データを付加します |
エンコーダー | HDMI_TX_ENCODER | 8B10B変換を行うエンコーダー |
パラシリ変換 | HDMI_TX_SERIALIZER | 10:1のシリアライザ |
- HS・・・水平同期信号
- VS・・・垂直同期信号
- DE・・・データイネーブル(有効画素フラグ)
- R・・・赤色データ(8bit)
- G・・・緑色データ(8bit)
- B・・・青色データ(8bit)
同期信号生成部
動作仕様
同期信号生成部は、画像同期信号を生成します。
画像同期信号の考え方自体は、VGA(アナログビデオ信号)のときと同様に考えてください。(ここでの説明は省略します)
Full-HDサイズ画像同期信号タイミングを下図に示します。
(FPGA内部の信号なのでHigh優位に統一しています)
ソースコード
- トリガのタイミング決定する"水平/垂直設定"はモジュール外からの入力とし、変更可能にする
- "水平/垂直設定"に矛盾した設定が入力された場合は動作保障外とする
IMAGE_SYNC.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity IMAGE_SYNC is
port(
i_pclk :in std_logic ;--ピクセルクロック
i_ena :in std_logic ;--同期信号出力イネーブル
i_h_cyc :in std_logic_vector(11 downto 0) ;--水平設定:周期
i_h_set :in std_logic_vector(11 downto 0) ;--水平設定:同期信号アサート
i_h_clr :in std_logic_vector(11 downto 0) ;--水平設定:同期信号ネゲート
i_h_act :in std_logic_vector(11 downto 0) ;--水平設定:有効期間開始
i_h_dis :in std_logic_vector(11 downto 0) ;--水平設定:有効期間終了
i_v_cyc :in std_logic_vector(11 downto 0) ;--垂直設定:周期
i_v_set :in std_logic_vector(11 downto 0) ;--垂直設定:同期信号アサート
i_v_clr :in std_logic_vector(11 downto 0) ;--垂直設定:同期信号ネゲート
i_v_act :in std_logic_vector(11 downto 0) ;--垂直設定:有効期間開始
i_v_dis :in std_logic_vector(11 downto 0) ;--垂直設定:有効期間終了
o_vs :out std_logic ;--垂直同期信号
o_hs :out std_logic ;--水平同期信号
o_de :out std_logic );--有効画素フラグ
end entity;
architecture top of IMAGE_SYNC is
--=====================================================
--TYPE
--=====================================================
--同期信号セットクリアタイミング
type typ_VIDEO_TRIGER is record
ena :std_logic ;--カウンタイネーブル
stt :std_logic ;--動作開始
fin :std_logic ;--1周期終了
inc :std_logic ;--カウンタインクリメント
set :std_logic ;--同期信号アサート
clr :std_logic ;--同期信号ネゲート
act :std_logic ;--有効期間開始
dis :std_logic ;--有効期間終了
end record;
--=====================================================
--SIGNAL
--=====================================================
signal ena :std_logic_vector(0 to 1) :="00" ;
signal ena_rise:std_logic ;
signal h_cyc :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal h_set :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal h_clr :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal h_act :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal h_dis :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal v_cyc :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal v_set :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal v_clr :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal v_act :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal v_dis :std_logic_vector(11 downto 0) :=(others=>'1') ;
signal h_cnt :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal v_cnt :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal h :typ_VIDEO_TRIGER ;
signal v :typ_VIDEO_TRIGER ;
signal vs :std_logic :='0' ;
signal hs :std_logic :='0' ;
signal ve :std_logic :='0' ;
signal de :std_logic :='0' ;
begin
--回路イネーブル微分FF
process(i_pclk)begin
if(Rising_edge(i_pclk))then
ena(0) <=i_ena;
ena(1) <=ena(0);
end if;
end process;
--回路イネーブル立上り
ena_rise <=ena(0) and (not ena(1));
--同期信号タイミング設定値ラッチ
process(i_pclk)begin
if(Rising_edge(i_pclk))then
if(ena_rise='1')then
h_cyc <=i_h_cyc;
h_set <=i_h_set;
h_clr <=i_h_clr;
h_act <=i_h_act;
h_dis <=i_h_dis;
v_cyc <=i_v_cyc;
v_set <=i_v_set;
v_clr <=i_v_clr;
v_act <=i_v_act;
v_dis <=i_v_dis;
end if;
end if;
end process;
--カウンタ
process(i_pclk)begin
if(Rising_edge(i_pclk))then
--水平カウンタ
if(h.stt='1')then
h_cnt <=conv_std_logic_vector(1,h_cnt'length);
elsif(h.ena='1')then
if(h.fin='1')then
h_cnt <=conv_std_logic_vector(1,h_cnt'length);
elsif(h.inc='1')then
h_cnt <=h_cnt+1;
else
h_cnt <=h_cnt;
end if;
else
h_cnt <=(others=>'0');
end if;
--垂直カウンタ
if(v.stt='1')then
v_cnt <=conv_std_logic_vector(1,v_cnt'length);
elsif(v.ena='1')then
if(v.fin='1')then
v_cnt <=conv_std_logic_vector(1,v_cnt'length);
elsif(v.inc='1')then
v_cnt <=v_cnt+1;
else
v_cnt <=v_cnt;
end if;
else
v_cnt <=(others=>'0');
end if;
end if;
end process;
--同期信号セットクリアタイミング
h.ena <=ena(1) ;
h.stt <=ena_rise ;
h.fin <='1' when(h_cnt=h_cyc)else '0' ;
h.inc <=ena(1) ;
h.set <='1' when(h_cnt=h_set)else '0' ;
h.clr <='1' when(h_cnt=h_clr)else '0' ;
h.act <='1' when(h_cnt=h_act)else '0' ;
h.dis <='1' when(h_cnt=h_dis)else '0' ;
v.ena <=ena(1) ;
v.stt <=ena_rise ;
v.fin <='1' when(v_cnt=v_cyc)else '0' ;
v.inc <=h.fin ;
v.set <='1' when(v_cnt=v_set)else '0' ;
v.clr <='1' when(v_cnt=v_clr)else '0' ;
v.act <='1' when(v_cnt=v_act)else '0' ;
v.dis <='1' when(v_cnt=v_dis)else '0' ;
--同期信号
process(i_pclk)begin
if(Rising_edge(i_pclk))then
--垂直同期信号
if(v.set='1')then
vs <='1';
elsif(v.clr='1')and(v.inc='1')then
vs <='0';
end if;
--垂直イネーブル
if(v.act='1')then
ve <='1';
elsif(v.dis='1')and(v.inc='1')then
ve <='0';
end if;
--水平同期信号
if(h.set='1')then
hs <='1';
elsif(h.clr='1')then
hs <='0';
end if;
--水平イネーブル(有効画素イネーブル)
if(h.act='1')then
de <=ve;
elsif(h.dis='1')then
de <='0';
end if;
end if;
end process;
--出力ポート
o_vs <=vs;
o_hs <=hs;
o_de <=de;
end architecture;
画像重畳部
画像重畳部はDEを検出して、水平/垂直画素カウントを数えています。
このカウントを利用してRGBの画素値に割り当てて、グラデーションを描いています。
(テスト用に適当に作ったものなので、詳しい説明は省略します。)
IMAGE_GEN.vhd
library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
entity IMAGE_GEN is
port(
i_sync_rst :in std_logic ;
i_pclk :in std_logic ;
i_vs :in std_logic ;
i_hs :in std_logic ;
i_de :in std_logic ;
o_img_vs :out std_logic ;
o_img_hs :out std_logic ;
o_img_de :out std_logic ;
o_img_red :out std_logic_vector(7 downto 0) ;
o_img_grn :out std_logic_vector(7 downto 0) ;
o_img_blu :out std_logic_vector(7 downto 0) );
end entity;
architecture top of IMAGE_GEN is
signal de :std_logic :='0' ;
signal vs :std_logic :='0' ;
signal line_end:std_logic ;
signal frm_stt :std_logic ;
signal v_cnt :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal h_cnt :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal red :std_logic_vector(7 downto 0) :=(others=>'0') ;
signal grn :std_logic_vector(7 downto 0) :=(others=>'0') ;
signal blu :std_logic_vector(7 downto 0) :=(others=>'0') ;
signal v_ptr :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal h_ptr :std_logic_vector(11 downto 0) :=(others=>'0') ;
signal v_dir :std_logic :='0' ;
signal h_dir :std_logic :='0' ;
--*********************************************
begin
--*********************************************
--ラインの終わり
line_end <=de and (not i_de);
--フレームの始まり
frm_stt <=(not vs) and i_vs;
-----------------------------------
--画素カウンタ
-----------------------------------
process(i_pclk)begin
if(rising_edge(i_pclk))then
--
de <=i_de;
vs <=i_vs;
--水平カウンタ
if(i_de='1')then
h_cnt <=h_cnt +1;
else
h_cnt <=(others=>'0');
end if;
--垂直カウンタ
if(i_vs='1')then
v_cnt <=(others=>'0');
else
if(line_end='1')then
v_cnt <=v_cnt +1;
else
v_cnt <=v_cnt;
end if;
end if;
end if;
end process;
-----------------------------------
--十字ポインタ
-----------------------------------
process(i_pclk)begin
if(rising_edge(i_pclk))then
if(frm_stt='1')then
if(v_dir='0')then
v_ptr <=v_ptr +1;
else
v_ptr <=v_ptr -1;
end if;
if(h_dir='0')then
h_ptr <=h_ptr +1;
else
h_ptr <=h_ptr -1;
end if;
if(v_ptr=conv_std_logic_vector(1079,12))then
v_dir <='1';
elsif(v_ptr=conv_std_logic_vector(0,12))then
v_dir <='0';
end if;
if(h_ptr=conv_std_logic_vector(1919,12))then
h_dir <='1';
elsif(h_ptr=conv_std_logic_vector(0,12))then
h_dir <='0';
end if;
end if;
end if;
end process;
-----------------------------------
--ダミー画像生成
-----------------------------------
process(i_pclk)begin
if(rising_edge(i_pclk))then
if(i_de='1')then
if(h_cnt=h_ptr)or(v_cnt=v_ptr)then
red <=x"00";
grn <=x"00";
blu <=x"00";
else
case(h_cnt(9 downto 8))is
when "00"=>
red <=h_cnt(7 downto 0);
grn <=v_cnt(7 downto 0);
blu <=x"00";
when "01"=>
red <=h_cnt(7 downto 0);
grn <=x"00";
blu <=v_cnt(7 downto 0);
when "10"=>
red <=x"00";
grn <=v_cnt(7 downto 0);
blu <=h_cnt(7 downto 0);
when others=>
red <=h_cnt(7 downto 0);
grn <=v_cnt(7 downto 0);
blu <=h_cnt(7 downto 0);
end case;
end if;
else
red <=x"00";
grn <=x"00";
blu <=x"00";
end if;
end if;
end process;
o_img_red <=red;
o_img_grn <=grn;
o_img_blu <=blu;
process(i_pclk,i_sync_rst)begin
if(i_sync_rst='1')then
o_img_vs <='0';
o_img_hs <='0';
o_img_de <='0';
elsif(rising_edge(i_pclk))then
o_img_vs <=i_vs;
o_img_hs <=i_hs;
o_img_de <=i_de;
end if;
end process;
end architecture;
その②へつづく