FPGAの部屋

FPGAやCPLDの話題やFPGA用のツールの話題などです。 マニアックです。 日記も書きます。

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。ご利用ください。 http://marsee101.web.fc2.com/index.html

カテゴリ: DVI, HDMI

DVI、HDMIの勉強5(キャラクタ・ディスプレイ・コントローラをDVI出力にする VHDL編2)”で完成したキャラクタ・ディスプレイ・コントローラをAXI4スレーブとして、XPSプロジェクトに加える前に、仕様の変更をすることにした。

AXI4バスはデータ幅32ビットとして実装するので、現在のキャラクタコード7ビット、R, G, Bのカラー情報1ビットずつ、計3ビットの合計10ビットでは、色ももっと出るのにもったいない。(”キャラクタ・ディスプレイ・コントローラの構成”参照)
これを16ビットに変更する。赤 (R) の色情報は 15~13ビット目の3ビット、緑 (R) の色情報は 12~10ビット目の3ビット、青 (B) の色情報は 9~7ビット目の3ビットとする。6~0ビット目はキャラクタコードとする。
これで16ビットになったので、32ビット幅に設定したAXI4バスのデータに2つのキャラクタがちょうど収まるようになる。
修正してインプリメントを行った。表示結果を下に示す。
34834b6e.jpg


全体に輝度が下がって、いろいろな色が表示されるようになった。

”DVI、HDMIの勉強4(キャラクタ・ディスプレイ・コントローラをDVI出力にする VHDL編1)”の続き。

今回は、SVGA(800x600)とXGA(1024x768) 解像度が表示できるかどうかを確かめてみた。おまけでSXGA(1280x1024)もやってみた。

・SVGA解像度
まずは、SVGAから。
修正部分を示す。画像のパラメータを記述してあるdisp_timing_parameters.vh をSVGA用に変更した。

// SVGA 解像度
parameter H_ACTIVE_VIDEO= 800;
parameter H_FRONT_PORCH = 40;
parameter H_SYNC_PULSE = 128;
parameter H_BACK_PORCH = 88;
parameter H_SUM = H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;

parameter V_ACTIVE_VIDEO = 600;
parameter V_FRONT_PORCH = 1;
parameter V_SYNC_PULSE = 4;
parameter V_BACK_PORCH = 23;
parameter V_SUM = V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;


次に、キャラクタを表示するCharDispCtrlerTest.vhd のアドレスをSVGA用に増やした。

    process(clk) begin -- キャラクタコードを+1して表示
        if clk'event and clk='1' then
            if reset='1' then
                processor_addr <= (others => '0');
            else
                if ena='1' then
                    -- if processor_addr=CONV_STD_LOGIC_VECTOR(4799, 13) then -- 終了(VGA)
                    if processor_addr=CONV_STD_LOGIC_VECTOR(7499, 13) then -- 終了(SVGA)
                        processor_addr <= (others => '0');
                    else
                        processor_addr <= processor_addr + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    


最後に、pixclk_gen のCLK_OUT2 を40MHz に変更した。
これでインプリメントしたらSVGA解像度で動作した。
45345467.jpg


・XGA解像度
次に、XGAでやってみた。
修正部分を示す。画像のパラメータを記述してあるdisp_timing_parameters.vh をXGA用に変更した。

parameter H_ACTIVE_VIDEO= 1024;
parameter H_FRONT_PORCH = 24;
parameter H_SYNC_PULSE = 136;
parameter H_BACK_PORCH = 160;
parameter H_SUM = H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;

parameter V_ACTIVE_VIDEO = 768;
parameter V_FRONT_PORCH = 2;
parameter V_SYNC_PULSE = 6;
parameter V_BACK_PORCH = 29;
parameter V_SUM = V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;


キャラクタを表示するCharDispCtrlerTest.vhd のアドレスをXGA用に増やそうとしたが、足りなかった。仕方がないので、最大の8191に変更した。XGA用には12,287に設定したいところだ。これで画面の下は、フレーム・バッファが足りずにミラー・イメージが出てしまう。

    process(clk) begin -- キャラクタコードを+1して表示
        if clk'event and clk='1' then
            if reset='1' then
                processor_addr <= (others => '0');
            else
                if ena='1' then
                    -- if processor_addr=CONV_STD_LOGIC_VECTOR(4799, 13) then -- 終了(VGA)
                    -- if processor_addr=CONV_STD_LOGIC_VECTOR(7499, 13) then -- 終了(SVGA)
                    if processor_addr=CONV_STD_LOGIC_VECTOR(8191, 13) then -- 足りないが終了(XGA)
                        processor_addr <= (others => '0');
                    else
                        processor_addr <= processor_addr + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;


pixclk_gen のCLK_OUT2 を65MHz に変更した。この変更により、CharDispCtrlerTest_HDMI.vhdのdvi_disp.vhd のgeneric map の値を変更した。65MHzを20倍すると1000MHzを超えてしまうので、PLL_CLKFBOUT_MULTを10倍に変更した。それに応じて他のパラメータも変更した。

    dvi_disp_inst : dvi_disp generic map (

        PLL_CLKFBOUT_MULT    => 10,    -- XGA
        PLL_CLKIN_PERIOD    => 15.4,
        PLL_CLKOUT0_DIVIDE    => 1,
        PLL_CLKOUT1_DIVIDE    => 10,
        PLL_CLKOUT2_DIVIDE    => 5
    ) port map (


これでインプリメントしたらXGA解像度で動作した。
bb7f7324.jpg



SVGAでもXGAでも問題なく表示できるようだ。
これでHDMIコネクタからDVI出力することができたので、SVGA解像度に戻して、キャラクタ・ディスプレイ・コントローラとして、AXI4バスにラッパーを作って接続してみようと思う。

(追加)
・SXGA解像度
SXGAはピクセルクロックが108MHzで10倍すると1080MHzなので、Spartan-6の-2スピードグレードだと、インプリメントでエラーになるが、-3スピードグレードに設定すると、インプリメントが通る。-2スピードグレードのFPGAを-3スピードグレードに偽って設定しているため、その他のタイミングでミスる可能性もあるが、やってみた。

画像のパラメータを記述してあるdisp_timing_parameters.vh をSXGA用に変更した。

// SXGA 解像度
parameter H_ACTIVE_VIDEO= 1280;
parameter H_FRONT_PORCH = 48;
parameter H_SYNC_PULSE = 112;
parameter H_BACK_PORCH = 248;
parameter H_SUM = H_ACTIVE_VIDEO + H_FRONT_PORCH + H_SYNC_PULSE + H_BACK_PORCH;

parameter V_ACTIVE_VIDEO = 1024;
parameter V_FRONT_PORCH = 1;
parameter V_SYNC_PULSE = 3;
parameter V_BACK_PORCH = 38;
parameter V_SUM = V_ACTIVE_VIDEO + V_FRONT_PORCH + V_SYNC_PULSE + V_BACK_PORCH;


キャラクタを表示するCharDispCtrlerTest.vhd のアドレスはXGAと同一だ。

CharDispCtrlerTest_HDMI.vhdのdvi_disp.vhd のgeneric map の値はXGAと同様に、PLL_CLKFBOUT_MULTを10倍に変更して、それに応じて他のパラメータも変更した。

pixclk_gen のCLK_OUT2 を108MHz に変更した。

インプリメントすると動作した。2時間くらい連続運転しているが大丈夫のようだ。案外行けるのかも?でも、ボード個体によってはダメかも知れないし、もっとリソースを使ってくるとだめになるかもしれない。いずれにせよリスキーだ。
下に、SXGA解像度の画面を貼っておく。
f139fd4a.jpg


SXGAになると、本当に字が細かいですね。見を凝らさないと見えません。(老眼です)

今回は、VHDLでキャラクタ・ディスプレイ・コントローラをAtlysボードのHDMIコネクタからDVI信号で出力してみた。VmodCAMのサンプルのVmodCAM_Ref_VGA Demo_13.zip を参照している。

VmodCAM_Ref_VGA Demo_13.zipを参照して、dvi_disp.vhd を作成して、VmodCAM_Ref_VGA Demo_13.zipの DVITransmittter.vhd, TMDSEncoder.vhd, SerializerN_1.vhd, Video.vhd を使用した。
CharDispCtler.v 以下はVerilog を使用している。VHDLも出来ているし、動作も確認してあるのだが、都合があるので公表していない。
下にProject Navigator の画面を示す。
12fcd208.png


インプリメントが成功したので、Atlysボードにダウンロードして試してみたところ、問題なく動作した。
14d5ee23.jpg


まずは、dvi_disp.vhd を下に貼る。このVHDLファイルを作成するにあたっては、VmodCAM_Ref_VGA Demo_13.zipを参照させて頂いてるが、自分で作りなおした。

-- dvi_disp.vhd
-- Verilog版とは違ってDigilent 社のDVITransmitter.vhd を使用する

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity dvi_disp is
    generic (
        PLL_CLKFBOUT_MULT : integer := 20;    -- VGA解像度 PLL VCO Freq 400MHz ~ 1000MHz
        PLL_CLKIN_PERIOD : real := 40.0;    -- VGA ピクセルクロック周期
        PLL_CLKOUT0_DIVIDE : integer := 2;    -- ピクセルクロックX10
        PLL_CLKOUT1_DIVIDE : integer := 20;    -- ピクセルクロック
        PLL_CLKOUT2_DIVIDE : integer := 10    -- ピクセルクロックX2
    );
    port (
        pixclk    :    in    std_logic;            -- pixel clock
        reset_in :    in     std_logic;            -- active high
        red_in :    in    std_logic_vector(7 downto 0);    -- RED入力
        green_in :    in    std_logic_vector(7 downto 0);    -- GREEN入力
        blue_in :    in    std_logic_vector(7 downto 0);    -- BLUE入力
        hsync :        in    std_logic;
        vsync :        in    std_logic;
        display_enable :    in std_logic;                -- 表示が有効
        TMDS_tx_clk_p :    out    std_logic;                    -- Clock
        TMDS_tx_clk_n :    out    std_logic;
        TMDS_tx_2_G_p :    out    std_logic;                    -- Green
        TMDS_tx_2_G_n :    out    std_logic;
        TMDS_tx_1_R_p :    out    std_logic;                    -- Red
        TMDS_tx_1_R_n :    out    std_logic;
        TMDS_tx_0_B_p :    out    std_logic;                    -- Blue
        TMDS_tx_0_B_n :    out    std_logic
    );
end dvi_disp;

architecture RTL of dvi_disp is
component PLL_BASE
  generic (
     BANDWIDTH : string := "OPTIMIZED";
     CLKFBOUT_MULT : integer := 1;
     CLKFBOUT_PHASE : real := 0.0;
     CLKIN_PERIOD : real := 0.000;
     CLKOUT0_DIVIDE : integer := 1;
     CLKOUT0_DUTY_CYCLE : real := 0.5;
     CLKOUT0_PHASE : real := 0.0;
     CLKOUT1_DIVIDE : integer := 1;
     CLKOUT1_DUTY_CYCLE : real := 0.5;
     CLKOUT1_PHASE : real := 0.0;
     CLKOUT2_DIVIDE : integer := 1;
     CLKOUT2_DUTY_CYCLE : real := 0.5;
     CLKOUT2_PHASE : real := 0.0;
     CLKOUT3_DIVIDE : integer := 1;
     CLKOUT3_DUTY_CYCLE : real := 0.5;
     CLKOUT3_PHASE : real := 0.0;
     CLKOUT4_DIVIDE : integer := 1;
     CLKOUT4_DUTY_CYCLE : real := 0.5;
     CLKOUT4_PHASE : real := 0.0;
     CLKOUT5_DIVIDE : integer := 1;
     CLKOUT5_DUTY_CYCLE : real := 0.5;
     CLKOUT5_PHASE : real := 0.0;
     CLK_FEEDBACK : string := "CLKFBOUT";
     COMPENSATION : string := "SYSTEM_SYNCHRONOUS";
     DIVCLK_DIVIDE : integer := 1;
     REF_JITTER : real := 0.100;
     RESET_ON_LOSS_OF_LOCK : boolean := FALSE
  );
  port (
     CLKFBOUT : out std_ulogic;
     CLKOUT0 : out std_ulogic;
     CLKOUT1 : out std_ulogic;
     CLKOUT2 : out std_ulogic;
     CLKOUT3 : out std_ulogic;
     CLKOUT4 : out std_ulogic;
     CLKOUT5 : out std_ulogic;
     LOCKED : out std_ulogic;
     CLKFBIN : in std_ulogic;
     CLKIN : in std_ulogic;
     RST : in std_ulogic
  );
end component;

component BUFPLL
  generic (
     DIVIDE : integer := 1;
     ENABLE_SYNC : boolean := TRUE
  );
  port (
     IOCLK : out std_ulogic;
     LOCK : out std_ulogic;
     SERDESSTROBE : out std_ulogic;
     GCLK : in std_ulogic;
     LOCKED : in std_ulogic;
     PLLIN : in std_ulogic
  );
end component;

component DVITransmitter
    port ( 
        RED_I : in  STD_LOGIC_VECTOR (7 downto 0);
        GREEN_I : in  STD_LOGIC_VECTOR (7 downto 0);
        BLUE_I : in  STD_LOGIC_VECTOR (7 downto 0);
        HS_I : in  STD_LOGIC;
        VS_I : in  STD_LOGIC;
        VDE_I : in  STD_LOGIC;
        PCLK_I : in  STD_LOGIC;
        PCLK_X2_I : in  STD_LOGIC;
        SERCLK_I : in  STD_LOGIC;
        SERSTB_I : in  STD_LOGIC;
        TMDS_TX_CLK_P : out  STD_LOGIC;
        TMDS_TX_CLK_N : out  STD_LOGIC;
        TMDS_TX_2_P : out  STD_LOGIC;
        TMDS_TX_2_N : out  STD_LOGIC;
        TMDS_TX_1_P : out  STD_LOGIC;
        TMDS_TX_1_N : out  STD_LOGIC;
        TMDS_TX_0_P : out  STD_LOGIC;
        TMDS_TX_0_N : out  STD_LOGIC
    );
end component;

component BUFG
  port (
     O : out std_ulogic;
     I : in std_ulogic
  );
end component;

signal pll_clkfg : std_logic;
signal pixel_clk, pixel_clkx10, pixel_clkx2 : std_logic;
signal pll_locked : std_logic;
signal pclk_bufg, pclkx2_bufg : std_logic;
signal bufpll_locked : std_logic;
signal serdes_strobe : std_logic;
signal pixel_clkio : std_logic;

begin
    PLL_BASE_PIXEL : PLL_BASE generic map (
        CLKFBOUT_MULT    => PLL_CLKFBOUT_MULT,
        COMPENSATION    => "INTERNAL",
        CLKIN_PERIOD    => PLL_CLKIN_PERIOD,
        CLKOUT0_DIVIDE    => PLL_CLKOUT0_DIVIDE,
        CLKOUT1_DIVIDE    => PLL_CLKOUT1_DIVIDE,
        CLKOUT2_DIVIDE    => PLL_CLKOUT2_DIVIDE
    ) port map (
        CLKFBOUT    => pll_clkfg,
        CLKOUT0        => pixel_clkx10,
        CLKOUT1        => pixel_clk,
        CLKOUT2        => pixel_clkx2,
        LOCKED        => pll_locked,
        CLKFBIN        => pll_clkfg,
        CLKIN        => pixclk,
        RST            => reset_in
    );
    
    BUFG_pixel_clk : BUFG port map (
        O    => pclk_bufg,
        I    => pixel_clk
    );
    
    BUFG_pixel_clkx2 : BUFG port map (
        O    => pclkx2_bufg,
        I    => pixel_clkx2
    );
    
    BUFPLL_pixel : BUFPLL generic map (
        DIVIDE    => 5,
        ENABLE_SYNC    => TRUE
    ) port map (
        IOCLK    => pixel_clkio,
        LOCK    => bufpll_locked,
        SERDESSTROBE    => serdes_strobe,
        GCLK    => pclkx2_bufg,
        LOCKED    => pll_locked,
        PLLIN    => pixel_clkx10
    );
    
    DVI_TX : DVITransmitter port map (
        RED_I        => red_in,
        GREEN_I        => green_in,
        BLUE_I        => blue_in,
        HS_I        => hsync,
        VS_I        => vsync,
        VDE_I        => display_enable,
        PCLK_I        => pclk_bufg,
        PCLK_X2_I    => pclkx2_bufg,
        SERCLK_I    => pixel_clkio,
        SERSTB_I    => serdes_strobe,
        TMDS_TX_CLK_P    => TMDS_tx_clk_p,
        TMDS_TX_CLK_N    => TMDS_tx_clk_n,
        TMDS_TX_2_P        => TMDS_tx_2_G_p,
        TMDS_TX_2_N        => TMDS_tx_2_G_n,
        TMDS_TX_1_P        => TMDS_tx_1_R_p,
        TMDS_TX_1_N        => TMDS_tx_1_R_n,
        TMDS_TX_0_P        => TMDS_tx_0_B_p,
        TMDS_TX_0_N        => TMDS_tx_0_B_n
    );
end RTL;


pixclk(ピクセルクロック)をもらって、PLL_BASEで、1倍、2倍、10倍のクロックを作り、2倍のクロックをBUFPLLに入れて、SERDES用のStrobeを作っている。OSERDES2は5ビットをシリアライズする。5ビットなので、Master-Slaveモードで使用している。10倍のクロックでシリアルデータを出し、2倍のクロックで動作させて、つまり10ビットをシリアライズしている。なぜ10ビットかというと、8B10Bエンコーダを使用しているからだ。
(参考文献:Spartan-6 FPGA クロック リソース ユーザー ガイド v1.3 (日本語版)(PDF, ver 1.6, 3.55 MB )、
        Spartan-6 FPGA SelectIO リソース ユーザー ガイド v1.3 (日本語版)(PDF, ver 1.4, 3.34 MB ))

次に、CharDispCtrlerTest.vhd を下に貼っておく。

-- CharDispCtrlerTest.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;

entity CharDispCtrlerTest is
    generic(
        ENABLE_COUNT    :    integer    :=    250000
    );
    port (
        clk            :    in    std_logic;
        reset        :    in    std_logic;
        VGA_RED        :    out    std_logic;
        VGA_GREEN    :    out    std_logic;
        VGA_BLUE    :    out    std_logic;
        VGA_HSYNC    :    out    std_logic;
        VGA_VSYNC    :    out    std_logic;
        display_enable : out std_logic
    );
end CharDispCtrlerTest;

architecture RTL of CharDispCtrlerTest is
component CharDispCtrler
    port(
        clk : in std_logic;
        reset : in std_logic;
        
        processor_addr : in std_logic_vector(12 downto 0);
        processor_din : in std_logic_vector(9 downto 0);
        processor_dout : out std_logic_vector(9 downto 0);
        processor_we : in std_logic;
        
        VGA_RED : out std_logic;
        VGA_GREEN : out std_logic;
        VGA_BLUE : out std_logic;
        VGA_HSYNC : out std_logic;
        VGA_VSYNC : out std_logic;
        display_enable : out std_logic
    );
end component;

signal processor_addr    :    std_logic_vector(12 downto 0);
signal processor_din    :     std_logic_vector(9 downto 0);
signal processor_dout    :    std_logic_vector(9 downto 0);
signal processor_we        :    std_logic;
signal count    :     std_logic_vector(22 downto 0);
signal ena        :    std_logic;
signal char_code    :    std_logic_vector(6 downto 0);
signal color_data    :    std_logic_vector(2 downto 0);

begin
    CharDispCtrler_inst : CharDispCtrler port map(
        clk        => clk,
        reset    => reset,
        processor_addr    => processor_addr,
        processor_din    => processor_din,
        processor_dout    => processor_dout,
        processor_we    => processor_we,
        VGA_RED            => VGA_RED,
        VGA_GREEN        => VGA_GREEN,
        VGA_BLUE        => VGA_BLUE,
        VGA_HSYNC        => VGA_HSYNC,
        VGA_VSYNC        => VGA_VSYNC,
        display_enable    => display_enable
    );
    
    process(clk) begin
        if clk'event and clk='1' then
            if reset='1' then
                count <= (others => '0');
                ena <= '0';
            else
                if count = ENABLE_COUNT then
                    count <= (others => '0');
                    ena <= '1';
                else
                    count <= count + 1;
                    ena <= '0';
                end if;
            end if;
        end if;
    end process;
    
    process(clk) begin -- キャラクタコードを+1して表示
        if clk'event and clk='1' then
            if reset='1' then
                processor_addr <= (others => '0');
            else
                if ena='1' then
                    if processor_addr=CONV_STD_LOGIC_VECTOR(4799, 13) then -- 終了
                        processor_addr <= (others => '0');
                    else
                        processor_addr <= processor_addr + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    process(clk) begin -- キャラクタコードを+1して表示
        if clk'event and clk='1' then
            if reset='1' then
                char_code <= "0100001"; -- キャラクタの!
            else
                if ena='1' then
                    if char_code="1111110" then -- キャラクタの~
                        char_code <= "0100001"; -- キャラクタの!
                    else
                        char_code <= char_code + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    process(clk) begin -- 色を+1しながら表示
        if clk'event and clk='1' then
            if reset='1' then
                color_data <= "001";
            else
                if ena='1' then
                    if color_data="111" then
                        color_data <= "001"; -- 0 は非表示
                    else
                        color_data <= color_data + 1;
                    end if;
                end if;
            end if;
        end if;
    end process;
    
    processor_din <= color_data & char_code;
    processor_we <= ena;
    
end RTL;


CharDispCtrlerTest_HDMI.vhd を貼っておく。

-- CharDispCtrlerTest_HDMI.vhd

library ieee;
use ieee.std_logic_1164.all;
use ieee.std_logic_unsigned.all;
use ieee.std_logic_arith.all;
library digilent;
use digilent.Video.ALL;

entity CharDispCtrlerTest_HDMI is
    generic(
        ENABLE_COUNT    :    integer    :=    250000
    );
    port(
        sysclk :    in    std_logic;
        reset_sw :    in    std_logic;
        TMDS_tx_clk_p :    out    std_logic;    -- Clock
        TMDS_tx_clk_n :    out    std_logic;
        TMDS_tx_2_G_p :    out    std_logic;    -- Green
        TMDS_tx_2_G_n :    out    std_logic;
        TMDS_tx_1_R_p :    out    std_logic;    -- Red
        TMDS_tx_1_R_n :    out    std_logic;
        TMDS_tx_0_B_p :    out    std_logic;    -- Blue
        TMDS_tx_0_B_n :    out    std_logic
    );
end CharDispCtrlerTest_HDMI;

architecture RTL of CharDispCtrlerTest_HDMI is
component pixclk_gen
    port (-- Clock in ports
        CLK_IN1           : in     std_logic;
        -- Clock out ports
        CLK_OUT1          : out    std_logic;
        CLK_OUT2          : out    std_logic;
        -- Status and control signals
        RESET             : in     std_logic;
        LOCKED            : out    std_logic
    );
end component;

component CharDispCtrlerTest
    generic(
        ENABLE_COUNT    :    integer    :=    250000
    );
    port (
        clk            :    in    std_logic;
        reset        :    in    std_logic;
        VGA_RED        :    out    std_logic;
        VGA_GREEN    :    out    std_logic;
        VGA_BLUE    :    out    std_logic;
        VGA_HSYNC    :    out    std_logic;
        VGA_VSYNC    :    out    std_logic;
        display_enable : out std_logic
    );
end component;

component dvi_disp 
    generic (
        PLL_CLKFBOUT_MULT : integer := 20;    -- VGA解像度 PLL VCO Freq 400MHz ~ 1000MHz
        PLL_CLKIN_PERIOD : real := 40.0;    -- VGA ピクセルクロック周期
        PLL_CLKOUT0_DIVIDE : integer := 2;    -- ピクセルクロックX10
        PLL_CLKOUT1_DIVIDE : integer := 20;    -- ピクセルクロック
        PLL_CLKOUT2_DIVIDE : integer := 10    -- ピクセルクロックX2
    );
    port (
        pixclk    :    in    std_logic;            -- pixel clock
        reset_in :    in     std_logic;            -- active high
        red_in :    in    std_logic_vector(7 downto 0);    -- RED入力
        green_in :    in    std_logic_vector(7 downto 0);    -- GREEN入力
        blue_in :    in    std_logic_vector(7 downto 0);    -- BLUE入力
        hsync :        in    std_logic;
        vsync :        in    std_logic;
        display_enable :    in std_logic;                -- 表示が有効
        TMDS_tx_clk_p :    out    std_logic;                    -- Clock
        TMDS_tx_clk_n :    out    std_logic;
        TMDS_tx_2_G_p :    out    std_logic;                    -- Green
        TMDS_tx_2_G_n :    out    std_logic;
        TMDS_tx_1_R_p :    out    std_logic;                    -- Red
        TMDS_tx_1_R_n :    out    std_logic;
        TMDS_tx_0_B_p :    out    std_logic;                    -- Blue
        TMDS_tx_0_B_n :    out    std_logic
    );
end component;

signal clk_100 : std_logic;
signal pixclk : std_logic;
signal reset : std_logic;
signal locked : std_logic;
signal vga_red, vga_green, vga_blue : std_logic;
signal vga_hsync, vga_vsync : std_logic;
signal display_enable : std_logic;
signal vga_r8, vga_g8, vga_b8 : std_logic_vector(7 downto 0);

begin
    pixclk_gen_inst : pixclk_gen port map (
        CLK_IN1        => sysclk,
        CLK_OUT1    => clk_100,
        CLK_OUT2    => pixclk,
        RESET        => reset_sw,
        LOCKED        => locked
    );
    reset <= not locked;
    
    CharDispCtrlerTest_inst : CharDispCtrlerTest generic map (
        ENABLE_COUNT => ENABLE_COUNT
    ) port map (
        clk            => pixclk,
        reset        => reset,
        VGA_RED        => vga_red,
        VGA_GREEN    => vga_green,
        VGA_BLUE    => vga_blue,
        VGA_HSYNC    => vga_hsync,
        VGA_VSYNC    => vga_vsync,
        display_enable    => display_enable
    );
    vga_r8 <= x"FF" when VGA_RED='1' else x"00";
    vga_g8 <= x"FF" when VGA_GREEN='1' else x"00";
    vga_b8 <= x"FF" when VGA_BLUE='1' else x"00";
    
    -- VGA解像度 PLL VCO Freq=400MHz ~ 1000MHz なので、25MHz入力クロックだと最初に20倍する必要がある。
    dvi_disp_inst : dvi_disp generic map (
        PLL_CLKFBOUT_MULT    => 20,
        PLL_CLKIN_PERIOD    => 40.0,
        PLL_CLKOUT0_DIVIDE    => 2,
        PLL_CLKOUT1_DIVIDE    => 20,
        PLL_CLKOUT2_DIVIDE    => 10
    ) port map (
        pixclk        => pixclk,
        reset_in    => reset,
        red_in        => vga_r8,
        green_in    => vga_g8,
        blue_in        => vga_b8,
        hsync        => vga_hsync,
        vsync        => vga_vsync,
        display_enable    => display_enable,
        TMDS_tx_clk_p    => TMDS_tx_clk_p,
        TMDS_tx_clk_n    => TMDS_tx_clk_n,
        TMDS_tx_2_G_p    => TMDS_tx_2_G_p,
        TMDS_tx_2_G_n    => TMDS_tx_2_G_n,
        TMDS_tx_1_R_p    => TMDS_tx_1_R_p,
        TMDS_tx_1_R_n    => TMDS_tx_1_R_n,
        TMDS_tx_0_B_p    => TMDS_tx_0_B_p,
        TMDS_tx_0_B_n    => TMDS_tx_0_B_n
    );
end RTL;


次に、CharDispCtrlerTest_HDMI.ucf を下に貼っておく。

NET "sysclk" LOC = L15;
NET "reset_sw" LOC = P3;
# Blue
NET "TMDS_tx_0_B_p" IOSTANDARD = TMDS_33;
NET "TMDS_tx_0_B_p" LOC = D8;
NET "TMDS_tx_0_B_n" IOSTANDARD = TMDS_33;
NET "TMDS_tx_0_B_n" LOC = C8;
# Red
NET "TMDS_tx_1_R_p" IOSTANDARD = TMDS_33;
NET "TMDS_tx_1_R_p" LOC = C7;
NET "TMDS_tx_1_R_n" IOSTANDARD = TMDS_33;
NET "TMDS_tx_1_R_n" LOC = A7;
# Green
NET "TMDS_tx_2_G_p" IOSTANDARD = TMDS_33;
NET "TMDS_tx_2_G_p" LOC = B8;
NET "TMDS_tx_2_G_n" IOSTANDARD = TMDS_33;
NET "TMDS_tx_2_G_n" LOC = A8;
# Clock
NET "TMDS_tx_clk_p" IOSTANDARD = TMDS_33;
NET "TMDS_tx_clk_p" LOC = B6;
NET "TMDS_tx_clk_n" IOSTANDARD = TMDS_33;
NET "TMDS_tx_clk_n" LOC = A6;

NET "sysclk" TNM_NET = "TNM_SYSCLK";
TIMESPEC TS_SYSCLK = PERIOD "TNM_SYSCLK" 100 MHz HIGH 50 % PRIORITY 0;


# PlanAhead Generated IO constraints

NET "reset_sw" IOSTANDARD = LVCMOS18;
NET "sysclk" IOSTANDARD = LVCMOS33;


プロジェクト中のpixclk_gen は100MHzのクロックを入力して、100MHzと25MHzを出力するようにClocking Wizard で作成した。
af82a009.png


7160bec1.png


ed269c0c.png


後のファイルは、”キャラクタ・ディスプレイ・コントローラのまとめ”からダウンロードすることができる。

(注)Digilent社の使用したVHDLファイルはdigilentライブラリを使用する。そのため、Project Navigator にAdd Source... で入れる時に、Library のところにdigilent と入れて、digilentライブラリを作成してVHDLファイルをプロジェクトに入れる必要があった。
DVI_HDMI_10_120222.png

現在、VHDL版を作成中だが、”DVI、HDMIの勉強4(キャラクタ・ディスプレイ・コントローラをDVI出力にする Verilog編)”のポートをVHDL版に合わせて変更した。TMDS信号の配列だけで、どのポートがどの色に対応するという情報があったほうが良いと思った。

インプリメントして、動作チェックをしたら問題なく動作した。

まずは、dvi_disp.v のポートマップから下に貼っておく。

// dvi_disp.v 
// DVI表示ユニット
// 

`default_nettype none

module dvi_disp #(
    parameter PLL_CLKFBOUT_MULT    = 20,    // VGA解像度 PLL VCO Freq=400MHz ~ 1000MHz
    parameter PLL_CLKIN_PERIOD     = 40.0,    // VGA ピクセルクロック周期
    parameter PLL_CLKOUT0_DIVIDE    = 2,    // ピクセルクロックX10
    parameter PLL_CLKOUT1_DIVIDE    = 20,    // ピクセルクロック
    parameter PLL_CLKOUT2_DIVIDE    = 10)    // ピクセルクロックX2
(
    input    wire    pixclk,                // pixel clock
    input    wire    reset_in,            // active high
    input    wire    [7:0]    red_in,        // RED入力
    input    wire    [7:0]    green_in,    // GREEN入力
    input    wire    [7:0]    blue_in,    // BLUE入力
    input    wire    hsync,
    input    wire    vsync,
    input    wire    display_enable,        // 表示が有効
    output    wire    TMDS_tx_clk_p,        // Clock
    output    wire    TMDS_tx_clk_n,
    output    wire    TMDS_tx_2_G_p,        // Green
    output    wire    TMDS_tx_2_G_n,
    output    wire    TMDS_tx_1_R_p,        // Red
    output    wire    TMDS_tx_1_R_n,
    output    wire    TMDS_tx_0_B_p,        // Blue
    output    wire    TMDS_tx_0_B_n
);


次に、CharDispCtrlerTest_HDMI.v を下に示す。
PLLのVCOの動作周波数は、Spartan-6の-2デバイスでは400MHz~1000MHzであるので、25MHzを10倍しても250MHzにしかならない。よって、最低動作周波数を下回ってしまう。そのため、PLLのVCOの動作周波数は、20倍して500MHzとしている。PLLのVCOの動作周波数範囲からSpartan-6の-2デバイスではSXGA(ピクセルクロック108MHz)の出力は難しいことがわかる。10倍すると1080MHzとなり、規格を超過してしまう。Spartan-6の通常の入出力ポートを使用したDVI出力でSXGAを出力するためには、-3のスピードグレードのSpartan-6が必要となる。(参考文献:Spartan-6 FPGA データシート : DC 特性およびスイッチ特性 v3.0 (日本語版)(PDF, ver 3.0, 2.09 MB )、58ページの表52 PLL仕様(続き))

// CharDispCtrlerTest_HDMI.v
// CharDispCtrlerTest.vのHDMI用のラッパー、HDMIコネクタから出力する

`default_nettype none

module CharDispCtrlerTest_HDMI (
    input    wire    sysclk,
    input    wire    reset_sw,
    output    wire    TMDS_tx_clk_p,        // Clock
    output    wire    TMDS_tx_clk_n,
    output    wire    TMDS_tx_2_G_p,        // Green
    output    wire    TMDS_tx_2_G_n,
    output    wire    TMDS_tx_1_R_p,        // Red
    output    wire    TMDS_tx_1_R_n,
    output    wire    TMDS_tx_0_B_p,        // Blue
    output    wire    TMDS_tx_0_B_n
);
    wire clk_100;
    wire pixclk;
    wire reset;
    wire locked;
    wire vga_red, vga_green, vga_blue;
    wire vga_hsync, vga_vsync;
    wire display_enable;
    
    pixclk_gen pixclk_gen_inst
    (// Clock in ports
        .CLK_IN1(sysclk),      // IN
        // Clock out ports
        .CLK_OUT1(clk_100),     // OUT
        .CLK_OUT2(pixclk),     // OUT
        // Status and control signals
        .RESET(reset_sw),// IN
        .LOCKED(locked)      // OUT
    );
    
    assign reset = !locked;
    
    CharDispCtrlerTest CharDispCtrlerTest_inst (
        .clk(pixclk),
        .reset(reset),
        .VGA_RED(vga_red),        // 1bit
        .VGA_GREEN(vga_green),    // 1bit
        .VGA_BLUE(vga_blue),    // 1bit
        .VGA_HSYNC(vga_hsync),
        .VGA_VSYNC(vga_vsync),
        .display_enable(display_enable)
    );

    // VGA解像度 PLL VCO Freq=400MHz ~ 1000MHz なので、25MHz入力クロックだと最初に20倍する必要がある。
    dvi_disp #(
        .PLL_CLKFBOUT_MULT(20),
        .PLL_CLKIN_PERIOD(40.0),
        .PLL_CLKOUT0_DIVIDE(2),
        .PLL_CLKOUT1_DIVIDE(20),
        .PLL_CLKOUT2_DIVIDE(10))
    dvi_disp_inst (
        .pixclk(pixclk),
        .reset_in(reset),
        .red_in({8{vga_red}}),
        .green_in({8{vga_green}}),
        .blue_in({8{vga_blue}}),
        .hsync(vga_hsync),
        .vsync(vga_vsync),
        .display_enable(display_enable),
        .TMDS_tx_clk_p(TMDS_tx_clk_p),
        .TMDS_tx_clk_n(TMDS_tx_clk_n),
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p),
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n),
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p),
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n),
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p),
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );
endmodule    

`default_nettype wire


CharDispCtrlerTest_HDMI_tb.v を下に貼っておく。

module CharDispCtrlerTest_HDMI_tb;

    // Inputs
    reg sysclk;
    reg reset_sw;

    // Outputs
    wire    TMDS_tx_clk_p;        // Clock
    wire    TMDS_tx_clk_n;
    wire    TMDS_tx_2_G_p;        // Green
    wire    TMDS_tx_2_G_n;
    wire    TMDS_tx_1_R_p;        // Red
    wire    TMDS_tx_1_R_n;
    wire    TMDS_tx_0_B_p;        // Blue
    wire    TMDS_tx_0_B_n;

    // Instantiate the Unit Under Test (UUT)
    CharDispCtrlerTest_HDMI uut (
        .sysclk(sysclk), 
        .reset_sw(reset_sw), 
        .TMDS_tx_clk_p(TMDS_tx_clk_p),
        .TMDS_tx_clk_n(TMDS_tx_clk_n),
        .TMDS_tx_2_G_p(TMDS_tx_2_G_p),
        .TMDS_tx_2_G_n(TMDS_tx_2_G_n),
        .TMDS_tx_1_R_p(TMDS_tx_1_R_p),
        .TMDS_tx_1_R_n(TMDS_tx_1_R_n),
        .TMDS_tx_0_B_p(TMDS_tx_0_B_p),
        .TMDS_tx_0_B_n(TMDS_tx_0_B_n)
    );

    parameter PERIOD = 10;
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;

    initial    // Clock process for clk
    begin
        #OFFSET;
        forever
        begin
            sysclk = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) sysclk = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end

    // Instantiate the Unit Under Test (UUT)
    defparam uut.CharDispCtrlerTest_inst.ENABLE_COUNT = 23'd000004; // シミュレーション

    initial begin
        // Initialize Inputs
        reset_sw = 1'b1;

        // Wait 100 ns for global reset to finish
        #100;
        reset_sw = 1'b0;
        
        // Add stimulus here
        #20000000    $stop;
    end
      
endmodule


DVI、HDMIの勉強3(XAPP495のvtc_demo.v を試す2)”の続き。

前回までで、vtc_demo.v を試すのは終了した。今回は、キャラクタ・ディスプレイ・コントローラをDVI出力にしてHDMIコネクタから画像を出力する。

まずは、vtc_demo.v を参考にして、R, G, Bとhsync, vsync を入れれば、TMDS信号(DVIやHDMIの信号)を出力する dvi_disp.v を作成した。Xilinxのライセンスの関係で、残念ながらソースは公開できない。ポートマップだけ下に公開する。

// dvi_disp.v 
// DVI表示ユニット
// 

`default_nettype none

module dvi_disp #(parameter CLKIN_PERIOD = 40.0) // VGA解像度
(
    input    wire    pixclk,                // pixel clock
    input    wire    reset_in,            // active high
    input    wire    [7:0]    red_in,        // RED入力
    input    wire    [7:0]    green_in,    // GREEN入力
    input    wire    [7:0]    blue_in,    // BLUE入力
    input    wire    hsync,
    input    wire    vsync,
    input    wire    display_enable,        // 表示が有効
    output    wire    [3:0]    TMDS,
    output    wire    [3:0]    TMDSB
);


これをキャラクタ・ディスプレイ・コントローラと合わせて、トップのファイルを作った。CharDispCtrlerTest_HDMI.v を下に示す。

// CharDispCtrlerTest_HDMI.v
// CharDispCtrlerTest.vのHDMI用のラッパー、HDMIコネクタから出力する

`default_nettype none

module CharDispCtrlerTest_HDMI (
    input    wire            sysclk,
    input    wire            reset_sw,
    output    wire    [3:0]    TMDS,
    output    wire    [3:0]    TMDSB
);
    wire clk_100;
    wire pixclk;
    wire reset;
    wire locked;
    wire vga_red, vga_green, vga_blue;
    wire vga_hsync, vga_vsync;
    wire display_enable;
    
    pixclk_gen pixclk_gen_inst
    (// Clock in ports
        .CLK_IN1(sysclk),      // IN
        // Clock out ports
        .CLK_OUT1(clk_100),     // OUT
        .CLK_OUT2(pixclk),     // OUT
        // Status and control signals
        .RESET(reset_sw),// IN
        .LOCKED(locked)      // OUT
    );
    
    assign reset = !locked;
    
    CharDispCtrlerTest CharDispCtrlerTest_inst (
        .clk(pixclk),
        .reset(reset),
        .VGA_RED(vga_red),        // 1bit
        .VGA_GREEN(vga_green),    // 1bit
        .VGA_BLUE(vga_blue),    // 1bit
        .VGA_HSYNC(vga_hsync),
        .VGA_VSYNC(vga_vsync),
        .display_enable(display_enable)
    );
    
    dvi_disp #(
        .CLKIN_PERIOD(40.0))
    dvi_disp_inst (
        .pixclk(pixclk),
        .reset_in(reset),
        .red_in({8{vga_red}}),
        .green_in({8{vga_green}}),
        .blue_in({8{vga_blue}}),
        .hsync(vga_hsync),
        .vsync(vga_vsync),
        .display_enable(display_enable),
        .TMDS(TMDS),
        .TMDSB(TMDSB)
    );
endmodule    

`default_nettype wire


pixclk_gen のCLK_OUT1は入力クロックそのままの100MHz、CLK_OUT2はピクセルクロックの25MHz(VGA解像度)を出力する。

更に、テストベンチ CharDispCtrlerTest_HDMI_tb.v も下に示す。”defparam uut.CharDispCtrlerTest_inst.ENABLE_COUNT = 23'd000004;”は、キャラクタを描画するタイミングを取るためのカウンタを階層をまたいで変更している。これは、通常ではシミュレーション時にキャラクタが表示されるのがとっても遅いため、カウンタ値を変更して表示を早めるために使用している。これが出来るのがVerilogの良い所でもあり、怖い所でもある。

module CharDispCtrlerTest_HDMI_tb;

    // Inputs
    reg sysclk;
    reg reset_sw;

    // Outputs
    wire [3:0] TMDS;
    wire [3:0] TMDSB;

    // Instantiate the Unit Under Test (UUT)
    CharDispCtrlerTest_HDMI uut (
        .sysclk(sysclk), 
        .reset_sw(reset_sw), 
        .TMDS(TMDS), 
        .TMDSB(TMDSB)
    );

    parameter PERIOD = 10;
    parameter real DUTY_CYCLE = 0.5;
    parameter OFFSET = 0;

    initial    // Clock process for clk
    begin
        #OFFSET;
        forever
        begin
            sysclk = 1'b0;
            #(PERIOD-(PERIOD*DUTY_CYCLE)) sysclk = 1'b1;
            #(PERIOD*DUTY_CYCLE);
        end
    end

    // Instantiate the Unit Under Test (UUT)
    defparam uut.CharDispCtrlerTest_inst.ENABLE_COUNT = 23'd000004; // シミュレーション

    initial begin
        // Initialize Inputs
        reset_sw = 1'b1;

        // Wait 100 ns for global reset to finish
        #100;
        reset_sw = 1'b0;
        
        // Add stimulus here
        #20000000    $stop;
    end
      
endmodule


下に100usec までのシミュレーション結果を示す。
cb08694f.png


あえて論評しない。

今度は、インプリメントしてみた。その結果を下に示す。
eb492fec.png


うまくインプリメントできたので、Atlysボードにダウンロードしたら、キャラクタ・ディスプレイ・コントローラが動作した。
14d5ee23.jpg


うまく行きました。
今回はVerilogだったので、Digilent社のVmodCAMのサンプルを参考にVHDLで書いてみようと思う。こっちはもう少しブログに書けるかと思う。

↑このページのトップヘ