FPGAの部屋 2012年02月22日
FC2ブログ

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

FPGAの部屋

FPGAの部屋の有用と思われるコンテンツのまとめサイトを作りました。Xilinx ISEの初心者の方には、FPGAリテラシーおよびチュートリアルのページをお勧めいたします。

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

今回は、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 の画面を示す。
DVI_HDMI_6_120221.png

インプリメントが成功したので、Atlysボードにダウンロードして試してみたところ、問題なく動作した。
DVI_HDMI_5_120218.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 で作成した。
DVI_HDMI_7_120222.png

DVI_HDMI_8_120222.png

DVI_HDMI_9_120222.png

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

(注)Digilent社の使用したVHDLファイルはdigilentライブラリを使用する。そのため、Project Navigator にAdd Source... で入れる時に、Library のところにdigilent と入れて、digilentライブラリを作成してVHDLファイルをプロジェクトに入れる必要があった。
DVI_HDMI_10_120222.png
  1. 2012年02月22日 04:52 |
  2. DVI, HDMI
  3. | トラックバック:0
  4. | コメント:0