FPGA
HDL
HDMI
xilinx
Spartan6

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

More than 1 year has passed since last update.

概要

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;

その②へつづく