Zedboard HDMI
From Hamsterworks Wiki!
This FPGA Project was started in January 2013.
See Zedboard_HDMI_v2 for an improved version, with working colour conversion
It is always fun to play with new interface, and the Zedboard has a ADV7511 HDMI transmitter on it. Rather than using the reference design available from the vendor I wanted to get HDMI going the hardware. I'm aiming for 1280x720 video mode.
The pins connected on the Zedboard are actually D8 through D23 (verified on schematic). The forces the use of the YCrCb colout space.
It isn't 100% yet, but getting there!
Contents |
Source files
The clean project is File:Zedboard hdmi.zip. If you build the project yourself, be sure to set "pack outputs into IOB".
zedboard_hdmi.vhd
---------------------------------------------------------------------------------- -- Engineer: Mike Field <hamster@snap.net.nz> -- -- Create Date: 06:01:06 01/23/2013 -- -- Description: -- Drive the ADV7511 HDMI encoder directly from the PL fabric -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; Library UNISIM; use UNISIM.vcomponents.all; entity zedboard_hdmi is Port ( clk_100 : in STD_LOGIC; hdmi_clk : out STD_LOGIC; hdmi_hsync : out STD_LOGIC; hdmi_vsync : out STD_LOGIC; hdmi_d : out STD_LOGIC_VECTOR (15 downto 0); hdmi_de : out STD_LOGIC; hdmi_int : in STD_LOGIC; hdmi_scl : out STD_LOGIC; hdmi_sda : inout STD_LOGIC); end zedboard_hdmi; architecture Behavioral of zedboard_hdmi is COMPONENT i2c_sender PORT( clk : IN std_logic; resend : IN std_logic; siod : INOUT std_logic; sioc : OUT std_logic ); END COMPONENT; signal blanking : std_logic := '0'; signal hsync : std_logic := '0'; signal vsync : std_logic := '0'; signal edge : std_logic := '0'; signal Y : STD_LOGIC_VECTOR (11 downto 0); signal Cr : STD_LOGIC_VECTOR (11 downto 0); signal Cb : STD_LOGIC_VECTOR (11 downto 0); signal hdmi_clk_bits : STD_LOGIC_VECTOR (1 downto 0); signal hcounter : unsigned(10 downto 0) := (others => '0'); signal vcounter : unsigned(10 downto 0) := (others => '0'); constant ZERO : unsigned(10 downto 0) := (others => '0'); signal hVisible : unsigned(10 downto 0); signal hStartSync : unsigned(10 downto 0); signal hEndSync : unsigned(10 downto 0); signal hMax : unsigned(10 downto 0); signal hSyncActive : std_logic := '1'; signal vVisible : unsigned(10 downto 0); signal vStartSync : unsigned(10 downto 0); signal vEndSync : unsigned(10 downto 0); signal vMax : unsigned(10 downto 0); signal vSyncActive : std_logic := '1'; signal clk_vgax2 : std_logic; signal clkfb : std_logic; signal clk : std_logic; begin -- Set the video mode to 1280x720x60Hz (75MHz pixel clock needed) hVisible <= ZERO + 1280; hStartSync <= ZERO + 1280+72; hEndSync <= ZERO + 1280+72+80; hMax <= ZERO + 1280+72+80+216-1; vSyncActive <= '1'; vVisible <= ZERO + 720; vStartSync <= ZERO + 720+3; vEndSync <= ZERO + 720+3+5; vMax <= ZERO + 720+3+5+22-1; hSyncActive <= '1'; Y <= std_logic_vector(hcounter(9 downto 0)) & "00"; Cr <= (std_logic_vector(hcounter(9 downto 0)+vcounter(9 downto 0))) & "00"; Cb <= std_logic_vector(vcounter(9 downto 0)) & "00"; vga_clkx2_process: process (clk_vgax2) begin if rising_edge(clk_VGAx2) then --------------------------------------------------------------------------- -- signal generation for the HDMI encoder -- -- Transfer on rising edge of clock Y -- on falling edge of clock Either Cr or Cb -- -- Because I am a wimp I don't use any DDR except for generating the DDR clk. ---------------------------------------------------------------------------- if edge = '0' then edge <= '1'; hdmi_clk_bits <= "00"; if blanking = '1' then hdmi_d <= (others => '0'); hdmi_de <= '0'; else hdmi_d <= Y & "0000"; hdmi_de <= '1'; end if; else edge <= '0'; hdmi_clk_bits <= "11"; if blanking = '1' then hdmi_d <= (others => '0'); hdmi_de <= '0'; else if hcounter(0) = '0' then hdmi_d <= Cr & "0000"; else hdmi_d <= Cb & "0000"; end if; hdmi_de <= '1'; end if; end if; hdmi_hsync <= hsync; hdmi_vsync <= vsync; ------------------------------------------------------------------------ -- VGA Signal Generation -- We only update when the second clock edge has been sent --- to the HDMI encoder ------------------------------------------------------------------------ if edge = '1' then if vcounter >= vVisible then blanking <= '1'; elsif hcounter >= hVisible then blanking <= '1'; else blanking <= '0'; end if; -- Generate the sync Pulses if vcounter = vStartSync then vSync <= vSyncActive; elsif vCounter = vEndSync then vSync <= not(vSyncActive); end if; if hcounter = hStartSync then hSync <= hSyncActive; elsif hCounter = hEndSync then hSync <= not(hSyncActive); end if; -- Advance the position counters IF hCounter = hMax THEN -- starting a new line hCounter <= (others => '0'); IF vCounter = vMax THEN vCounter <= (others => '0'); ELSE vCounter <= vCounter + 1; END IF; ELSE hCounter <= hCounter + 1; END IF; end if; end if; end process; ODDR_inst : ODDR generic map( DDR_CLK_EDGE => "OPPOSITE_EDGE", INIT => '0',SRTYPE => "SYNC") port map ( Q => hdmi_clk, C => clk_VGAx2, D1 => hdmi_clk_bits(0), D2 => hdmi_clk_bits(1), CE => '1', R => '0', S => '0' ); Inst_i2c_sender: i2c_sender PORT MAP( clk => clk, resend => '0', sioc => hdmi_scl, siod => hdmi_sda ); -- Generate a 130MHz and 100Mhz clock from the input. PLLE2_BASE_inst : PLLE2_BASE generic map ( BANDWIDTH => "OPTIMIZED", -- OPTIMIZED, HIGH, LOW CLKFBOUT_MULT => 9, -- Multiply value for all CLKOUT, (2-64) CLKFBOUT_PHASE => 0.0, -- Phase offset in degrees of CLKFB, (-360.000-360.000). CLKIN1_PERIOD => 10.0, -- Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). -- CLKOUT0_DIVIDE - CLKOUT5_DIVIDE: Divide amount for each CLKOUT (1-128) CLKOUT0_DIVIDE => 9, CLKOUT1_DIVIDE => 6, CLKOUT2_DIVIDE => 1, CLKOUT3_DIVIDE => 1, CLKOUT4_DIVIDE => 1, CLKOUT5_DIVIDE => 1, -- CLKOUT0_DUTY_CYCLE - CLKOUT5_DUTY_CYCLE: Duty cycle for each CLKOUT (0.001-0.999). CLKOUT0_DUTY_CYCLE => 0.5, CLKOUT1_DUTY_CYCLE => 0.5, CLKOUT2_DUTY_CYCLE => 0.5, CLKOUT3_DUTY_CYCLE => 0.5, CLKOUT4_DUTY_CYCLE => 0.5, CLKOUT5_DUTY_CYCLE => 0.5, -- CLKOUT0_PHASE - CLKOUT5_PHASE: Phase offset for each CLKOUT (-360.000-360.000). CLKOUT0_PHASE => 0.0, CLKOUT1_PHASE => 0.0, CLKOUT2_PHASE => 0.0, CLKOUT3_PHASE => 0.0, CLKOUT4_PHASE => 0.0, CLKOUT5_PHASE => 0.0, DIVCLK_DIVIDE => 1, -- Master division value, (1-56) REF_JITTER1 => 0.0, -- Reference input jitter in UI, (0.000-0.999). STARTUP_WAIT => "FALSE" -- Delay DONE until PLL Locks, ("TRUE"/"FALSE") ) port map ( -- Clock Outputs: 1-bit (each) output: User configurable clock outputs CLKOUT0 => clk, CLKOUT1 => clk_VGAx2, CLKOUT2 => open, CLKOUT3 => open, CLKOUT4 => open, CLKOUT5 => open, CLKFBOUT => clkfb, -- 1-bit output: Feedback clock LOCKED => open, -- 1-bit output: LOCK CLKIN1 => clk_100, -- 1-bit input: Input clock PWRDWN => '0', -- 1-bit input: Power-down RST => '0', -- 1-bit input: Reset CLKFBIN => clkfb -- 1-bit input: Feedback clock ); end Behavioral;
zedboard_.ucf
NET CLK_100 LOC = Y9 |IOSTANDARD=LVCMOS33; # "GCLK" NET "clk_100" TNM_NET = clk_100; TIMESPEC TS_clk_100 = PERIOD "clk_100" 10 ns HIGH 50%; NET HDMI_CLK LOC = W18 | IOSTANDARD=LVCMOS33; # "HD-CLK" NET HDMI_D<0> LOC = Y13 | IOSTANDARD=LVCMOS33; # "HD-D0" NET HDMI_D<1> LOC = AA13 | IOSTANDARD=LVCMOS33; # "HD-D1" NET HDMI_D<2> LOC = AA14 | IOSTANDARD=LVCMOS33; # "HD-D2" NET HDMI_D<3> LOC = Y14 | IOSTANDARD=LVCMOS33; # "HD-D3" NET HDMI_D<4> LOC = AB15 | IOSTANDARD=LVCMOS33; # "HD-D4" NET HDMI_D<5> LOC = AB16 | IOSTANDARD=LVCMOS33; # "HD-D5" NET HDMI_D<6> LOC = AA16 | IOSTANDARD=LVCMOS33; # "HD-D6" NET HDMI_D<7> LOC = AB17 | IOSTANDARD=LVCMOS33; # "HD-D7" NET HDMI_D<8> LOC = AA17 | IOSTANDARD=LVCMOS33; # "HD-D8" NET HDMI_D<9> LOC = Y15 | IOSTANDARD=LVCMOS33; # "HD-D9" NET HDMI_D<10> LOC = W13 | IOSTANDARD=LVCMOS33; # "HD-D10" NET HDMI_D<11> LOC = W15 | IOSTANDARD=LVCMOS33; # "HD-D11" NET HDMI_D<12> LOC = V15 | IOSTANDARD=LVCMOS33; # "HD-D12" NET HDMI_D<13> LOC = U17 | IOSTANDARD=LVCMOS33; # "HD-D13" NET HDMI_D<14> LOC = V14 | IOSTANDARD=LVCMOS33; # "HD-D14" NET HDMI_D<15> LOC = V13 | IOSTANDARD=LVCMOS33; # "HD-D15" NET HDMI_DE LOC = U16 | IOSTANDARD=LVCMOS33; # "HD-DE" NET HDMI_HSYNC LOC = V17 | IOSTANDARD=LVCMOS33; # "HD-HSYNC" NET HDMI_VSYNC LOC = W17 | IOSTANDARD=LVCMOS33; # "HD-VSYNC" NET HDMI_INT LOC = W16 | IOSTANDARD=LVCMOS33; # "HD-INT" NET HDMI_SCL LOC = AA18 | IOSTANDARD=LVCMOS33; # "HD-SCL" NET HDMI_SDA LOC = Y16 | IOSTANDARD=LVCMOS33 | PULLUP; # "HD-SDA" #NET HDMI_SPDIF LOC = U15 | IOSTANDARD=LVCMOS33; # "HD-SPDIF" #NET HDMI_SPDIFO LOC = Y18 | IOSTANDARD=LVCMOS33; # "HD-SPDIFO"
i2c_sender.vhd
---------------------------------------------------------------------------------- -- Engineer: <mfield@concepts.co.nz -- -- Description: Send register writes over an I2C-like interface -- ---------------------------------------------------------------------------------- library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity i2c_sender is Port ( clk : in STD_LOGIC; resend : in STD_LOGIC; sioc : out STD_LOGIC; siod : inout STD_LOGIC ); end i2c_sender; architecture Behavioral of i2c_sender is signal divider : unsigned(7 downto 0) := (others => '0'); -- this value gives nearly 200ms cycles before the first register is written signal initial_pause : unsigned(22 downto 0) := (others => '0'); signal finished : std_logic := '0'; signal address : std_logic_vector(7 downto 0) := (others => '0'); signal clk_first_quarter : std_logic_vector(28 downto 0) := (others => '1'); signal clk_last_quarter : std_logic_vector(28 downto 0) := (others => '1'); signal busy_sr : std_logic_vector(28 downto 0) := (others => '1'); signal data_sr : std_logic_vector(28 downto 0) := (others => '1'); signal tristate_sr : std_logic_vector(28 downto 0) := (others => '0'); signal reg_value : std_logic_vector(15 downto 0) := (others => '0'); constant i2c_wr_addr : std_logic_vector(7 downto 0) := x"72"; begin registers: process(clk) begin if rising_edge(clk) then case address is when x"00" => reg_value <= x"4100"; -- Powerup please when x"01" => reg_value <= x"9803"; -- Must be set to this when x"02" => reg_value <= x"9AE0"; -- Must be set to this when x"03" => reg_value <= x"9C30"; -- Must be set to this when x"04" => reg_value <= x"9D61"; -- Must be set to this when x"05" => reg_value <= x"A2A4"; -- Must be set to this when x"06" => reg_value <= x"A3A4"; -- Must be set to this when x"07" => reg_value <= x"E0D0"; -- Must be set to this when x"08" => reg_value <= x"5512"; -- Must be set to this when x"09" => reg_value <= x"F900"; -- Must be set to this when x"0a" => reg_value <= x"9d01"; -- pixel divider when x"0b" => reg_value <= x"1506"; -- YCbCr 422, DDR, External sync when x"0c" => reg_value <= x"4810"; -- Right aligned data (D23 downto 8) when x"0d" => reg_value <= x"1636"; -- 8 bit style 2, 1st half on rising edge - RGB output when x"0e" => reg_value <= x"1700"; -- output aspect ratio 16:9, external DE when x"0f" => reg_value <= x"1800"; -- CSC off when x"10" => reg_value <= x"AF00"; -- DVI mode when x"11" => reg_value <= x"4c04"; -- Deep colour (HDMI only?) when x"12" => reg_value <= x"4000"; -- Turn off additional data packets when others => reg_value <= x"ffff"; end case; end if; end process; i2c_tristate: process(data_sr, tristate_sr) begin if tristate_sr(tristate_sr'length-1) = '0' then siod <= data_sr(data_sr'length-1); else siod <= 'Z'; end if; end process; with divider(divider'length-1 downto divider'length-2) select sioc <= clk_first_quarter(clk_first_quarter'length -1) when "00", clk_last_quarter(clk_last_quarter'length -1) when "11", '1' when others; i2c_send: process(clk) begin if rising_edge(clk) then if resend = '1' then address <= (others => '0'); clk_first_quarter <= (others => '1'); clk_last_quarter <= (others => '1'); busy_sr <= (others => '0'); divider <= (others => '0'); initial_pause <= (others => '0'); finished <= '0'; end if; if busy_sr(busy_sr'length-1) = '0' then if initial_pause(initial_pause'length-1) = '0' then initial_pause <= initial_pause+1; elsif finished = '0' then if divider = "11111111" then divider <= (others =>'0'); if reg_value(15 downto 8) = "11111111" then finished <= '1'; else -- move the new data into the shift registers clk_first_quarter <= (others => '0'); clk_first_quarter(clk_first_quarter'length-1) <= '1'; clk_last_quarter <= (others => '0'); clk_last_quarter(0) <= '1'; -- Start Address Ack Register Ack Value Ack Stop tristate_sr <= "0" & "00000000" & "1" & "00000000" & "1" & "00000000" & "1" & "0"; data_sr <= "0" & i2c_wr_addr & "1" & reg_value(15 downto 8) & "1" & reg_value( 7 downto 0) & "1" & "0"; busy_sr <= (others => '1'); address <= std_logic_vector(unsigned(address)+1); end if; else divider <= divider+1; end if; end if; else if divider = "11111111" then -- divide clkin by 255 for I2C tristate_sr <= tristate_sr(tristate_sr'length-2 downto 0) & '0'; busy_sr <= busy_sr(busy_sr'length-2 downto 0) & '0'; data_sr <= data_sr(data_sr'length-2 downto 0) & '1'; clk_first_quarter <= clk_first_quarter(clk_first_quarter'length-2 downto 0) & '1'; clk_last_quarter <= clk_last_quarter(clk_first_quarter'length-2 downto 0) & '1'; divider <= (others => '0'); else divider <= divider+1; end if; end if; end if; end process; end Behavioral;