LoginSignup
24
18

More than 5 years have passed since last update.

FPGAでHDMI出力(Full-HD):その①同期信号/画素データ生成

Last updated at Posted at 2017-10-11

概要

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のシリアライザ

HDMI_1.jpg

  • HS・・・水平同期信号
  • VS・・・垂直同期信号
  • DE・・・データイネーブル(有効画素フラグ)
  • R・・・赤色データ(8bit)
  • G・・・緑色データ(8bit)
  • B・・・青色データ(8bit)

同期信号生成部

動作仕様

同期信号生成部は、画像同期信号を生成します。
画像同期信号の考え方自体は、VGA(アナログビデオ信号)のときと同様に考えてください。(ここでの説明は省略します)

Full-HDサイズ画像同期信号タイミングを下図に示します。
(FPGA内部の信号なのでHigh優位に統一しています)

HDMI_2.jpg

ソースコード

  • トリガのタイミング決定する"水平/垂直設定"はモジュール外からの入力とし、変更可能にする
  • "水平/垂直設定"に矛盾した設定が入力された場合は動作保障外とする
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;

その②へつづく

24
18
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
24
18