commit 89ee75121b21ef2a163e67ed4933ef4fbef76c6d Author: Mike Field Date: Tue Aug 4 22:27:00 2015 +1200 Initial version diff --git a/Artix-7-HDMI-processing.cache/wt/java_command_handlers.wdf b/Artix-7-HDMI-processing.cache/wt/java_command_handlers.wdf new file mode 100644 index 0000000..1d8c903 --- /dev/null +++ b/Artix-7-HDMI-processing.cache/wt/java_command_handlers.wdf @@ -0,0 +1,3 @@ +version:1 +70726f6a656374:706c616e5f61686561645f75736167655c6a6176615f636f6d6d616e645f68616e646c657273:6e657770726f6a656374:2d31:00:00 +eof:625436222 diff --git a/Artix-7-HDMI-processing.cache/wt/webtalk_pa.xml b/Artix-7-HDMI-processing.cache/wt/webtalk_pa.xml new file mode 100644 index 0000000..736d475 --- /dev/null +++ b/Artix-7-HDMI-processing.cache/wt/webtalk_pa.xml @@ -0,0 +1,29 @@ + + + + +
+ + +
+
+ + + + + + + + + + + + + + + +
+
+
diff --git a/Artix-7-HDMI-processing.xpr b/Artix-7-HDMI-processing.xpr new file mode 100644 index 0000000..8828d49 --- /dev/null +++ b/Artix-7-HDMI-processing.xpr @@ -0,0 +1,265 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Vivado Synthesis Defaults + + + + + + + + Vivado Implementation Defaults + + + + + + + + + + + + + + diff --git a/README.txt b/README.txt new file mode 100644 index 0000000..3f16f11 --- /dev/null +++ b/README.txt @@ -0,0 +1,82 @@ +README file for Artix 7 HDMI processing +======================================= +Hi! + +This is my design for receiving HDMI input, then extracting the video data, the +Video Inforframe and audio samples, then using that to display audio db meters +on the top corner of the screen. Currently for simplicity the output is only DVID. + +Features +-------- +Supports HDMI formats: + -720p@50 + - 720p@60, + - 1080i (with a bug) + - 1080p@50 + - 1080p@60 + and others.... + +Colourspaces / formats: + - RGB 444 + - YCbCr 444 + - YCbCr 422 + +Supported Boards +---------------- + - Digilent Nexys Video + +Sources tested with: + - Western Digital HD Live + - HP Laptop + +Sinks tested with: + - Viewsonic Monitor + - AOC Monitor + - Vivo TV + +Known issues: + - Currently extracts only two channels of audio + - Does not adjust PLL settings for input clock, so the PLL is run slightly out + of spec. + - Image may re-sync after a few seconds if it receives errors. + - The audio meters are drawn twice as tall in interlaced modes + - A false VSYNC pulse is causing the meters to be displayed more than once. + +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- diff --git a/constraints/NexysVideo.xdc b/constraints/NexysVideo.xdc new file mode 100644 index 0000000..c0c20d6 --- /dev/null +++ b/constraints/NexysVideo.xdc @@ -0,0 +1,69 @@ +#------------------------------------------------------------------------------------ +# HDMI and clock Constraints for the Digilent Nexys Video FPGA development board. +#------------------------------------------------------------------------------------ + +##Clock Signal +set_property -dict { PACKAGE_PIN R4 IOSTANDARD LVCMOS33 } [get_ports { clk100 }]; + create_clock -add -name sys_clk_pin -period 10.00 -waveform {0 5} [get_ports clk100] + +##HDMI in +create_clock -add -name hdmi_clk -period 6.7 -waveform {0 5} [get_ports hdmi_rx_clk_p] + +set_property -dict { PACKAGE_PIN AA5 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_cec }]; #IO_L10P_T1_34 Sch=hdmi_rx_cec +set_property -dict { PACKAGE_PIN W4 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_clk_n }]; #IO_L12N_T1_MRCC_34 Sch=hdmi_rx_clk_n +set_property -dict { PACKAGE_PIN V4 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_clk_p }]; #IO_L12P_T1_MRCC_34 Sch=hdmi_rx_clk_p +set_property -dict { PACKAGE_PIN AB12 IOSTANDARD LVCMOS25 } [get_ports { hdmi_rx_hpa }]; #IO_L7N_T1_13 Sch=hdmi_rx_hpa +set_property -dict { PACKAGE_PIN Y4 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_scl }]; #IO_L11P_T1_SRCC_34 Sch=hdmi_rx_scl +set_property -dict { PACKAGE_PIN AB5 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_sda }]; #IO_L10N_T1_34 Sch=hdmi_rx_sda +set_property -dict { PACKAGE_PIN R3 IOSTANDARD LVCMOS33 } [get_ports { hdmi_rx_txen }]; #IO_L3P_T0_DQS_34 Sch=hdmi_rx_txen +set_property -dict { PACKAGE_PIN AA3 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[0] }]; #IO_L9N_T1_DQS_34 Sch=hdmi_rx_n[0] +set_property -dict { PACKAGE_PIN Y3 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[0] }]; #IO_L9P_T1_DQS_34 Sch=hdmi_rx_p[0] +set_property -dict { PACKAGE_PIN Y2 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[1] }]; #IO_L4N_T0_34 Sch=hdmi_rx_n[1] +set_property -dict { PACKAGE_PIN W2 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[1] }]; #IO_L4P_T0_34 Sch=hdmi_rx_p[1] +set_property -dict { PACKAGE_PIN V2 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_n[2] }]; #IO_L2N_T0_34 Sch=hdmi_rx_n[2] +set_property -dict { PACKAGE_PIN U2 IOSTANDARD TMDS_33 } [get_ports { hdmi_rx_p[2] }]; #IO_L2P_T0_34 Sch=hdmi_rx_p[2] + + +##HDMI out +set_property -dict { PACKAGE_PIN AA4 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_cec }]; #IO_L11N_T1_SRCC_34 Sch=hdmi_tx_cec +set_property -dict { PACKAGE_PIN U1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_n }]; #IO_L1N_T0_34 Sch=hdmi_tx_clk_n +set_property -dict { PACKAGE_PIN T1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_clk_p }]; #IO_L1P_T0_34 Sch=hdmi_tx_clk_p +set_property -dict { PACKAGE_PIN AB13 IOSTANDARD LVCMOS25 } [get_ports { hdmi_tx_hpd }]; #IO_L3N_T0_DQS_13 Sch=hdmi_tx_hpd +set_property -dict { PACKAGE_PIN U3 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_rscl }]; #IO_L6P_T0_34 Sch=hdmi_tx_rscl +set_property -dict { PACKAGE_PIN V3 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_rsda }]; #IO_L6N_T0_VREF_34 Sch=hdmi_tx_rsda +set_property -dict { PACKAGE_PIN Y1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[0] }]; #IO_L5N_T0_34 Sch=hdmi_tx_n[0] +set_property -dict { PACKAGE_PIN W1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[0] }]; #IO_L5P_T0_34 Sch=hdmi_tx_p[0] +set_property -dict { PACKAGE_PIN AB1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[1] }]; #IO_L7N_T1_34 Sch=hdmi_tx_n[1] +set_property -dict { PACKAGE_PIN AA1 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[1] }]; #IO_L7P_T1_34 Sch=hdmi_tx_p[1] +set_property -dict { PACKAGE_PIN AB2 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_n[2] }]; #IO_L8N_T1_34 Sch=hdmi_tx_n[2] +set_property -dict { PACKAGE_PIN AB3 IOSTANDARD TMDS_33 } [get_ports { hdmi_tx_p[2] }]; #IO_L8P_T1_34 Sch=hdmi_tx_p[2] + +# DEBUG on JA +set_property -dict { PACKAGE_PIN AB22 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[0] }]; #IO_L10N_T1_D15_14 Sch=ja[1] +set_property -dict { PACKAGE_PIN AB21 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[1] }]; #IO_L10P_T1_D14_14 Sch=ja[2] +set_property -dict { PACKAGE_PIN AB20 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[2] }]; #IO_L15N_T2_DQS_DOUT_CSO_B_14 Sch=ja[3] +set_property -dict { PACKAGE_PIN AB18 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[3] }]; #IO_L17N_T2_A13_D29_14 Sch=ja[4] +set_property -dict { PACKAGE_PIN Y21 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[4] }]; #IO_L9P_T1_DQS_14 Sch=ja[7] +set_property -dict { PACKAGE_PIN AA21 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[5] }]; #IO_L8N_T1_D12_14 Sch=ja[8] +set_property -dict { PACKAGE_PIN AA20 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[6] }]; #IO_L8P_T1_D11_14 Sch=ja[9] +set_property -dict { PACKAGE_PIN AA18 IOSTANDARD LVCMOS33 } [get_ports { debug_pmod[7] }]; #IO_L17P_T2_A14_D30_14 Sch=ja[10 + +##Switches +set_property -dict { PACKAGE_PIN E22 IOSTANDARD LVCMOS25 } [get_ports { sw[0] }]; #IO_L22P_T3_16 Sch=sw[0] +set_property -dict { PACKAGE_PIN F21 IOSTANDARD LVCMOS25 } [get_ports { sw[1] }]; #IO_25_16 Sch=sw[1] +set_property -dict { PACKAGE_PIN G21 IOSTANDARD LVCMOS25 } [get_ports { sw[2] }]; #IO_L24P_T3_16 Sch=sw[2] +#set_property -dict { PACKAGE_PIN G22 IOSTANDARD LVCMOS25 } [get_ports { switches[3] }]; #IO_L24N_T3_16 Sch=sw[3] +#set_property -dict { PACKAGE_PIN H17 IOSTANDARD LVCMOS25 } [get_ports { switches[4] }]; #IO_L6P_T0_15 Sch=sw[4] +#set_property -dict { PACKAGE_PIN J16 } [get_ports { sw[5] }]; #IO_0_15 Sch=sw[5] +#set_property -dict { PACKAGE_PIN K13 } [get_ports { sw[6] }]; #IO_L19P_T3_A22_15 Sch=sw[6] +#set_property -dict { PACKAGE_PIN M17 } [get_ports { sw[7] }]; #IO_25_15 Sch=sw[7] + +##LEDs +set_property -dict { PACKAGE_PIN T14 IOSTANDARD LVCMOS25 } [get_ports { led[0] }]; #IO_L15P_T2_DQS_13 Sch=led[0] +set_property -dict { PACKAGE_PIN T15 IOSTANDARD LVCMOS25 } [get_ports { led[1] }]; #IO_L15N_T2_DQS_13 Sch=led[1] +set_property -dict { PACKAGE_PIN T16 IOSTANDARD LVCMOS25 } [get_ports { led[2] }]; #IO_L17P_T2_13 Sch=led[2] +set_property -dict { PACKAGE_PIN U16 IOSTANDARD LVCMOS25 } [get_ports { led[3] }]; #IO_L17N_T2_13 Sch=led[3] +set_property -dict { PACKAGE_PIN V15 IOSTANDARD LVCMOS25 } [get_ports { led[4] }]; #IO_L14N_T2_SRCC_13 Sch=led[4] +set_property -dict { PACKAGE_PIN W16 IOSTANDARD LVCMOS25 } [get_ports { led[5] }]; #IO_L16N_T2_13 Sch=led[5] +set_property -dict { PACKAGE_PIN W15 IOSTANDARD LVCMOS25 } [get_ports { led[6] }]; #IO_L16P_T2_13 Sch=led[6] +set_property -dict { PACKAGE_PIN Y13 IOSTANDARD LVCMOS25 } [get_ports { led[7] }]; #IO_L5P_T0_13 Sch=led[7] \ No newline at end of file diff --git a/src/alingment_detect.vhd b/src/alingment_detect.vhd new file mode 100644 index 0000000..69eb0aa --- /dev/null +++ b/src/alingment_detect.vhd @@ -0,0 +1,163 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field '0'); + signal signal_quality : unsigned(27 downto 0) := (others => '0'); + signal holdoff : unsigned(9 downto 0) := (others => '0'); + signal error_seen : std_logic := '0'; + signal idelay_ce : std_logic := '0'; + signal idelay_count : std_logic_vector(4 downto 0) := (others => '0'); + signal symbol_sync_i : std_logic := '0'; + +begin + delay_count <= idelay_count; + delay_ce <= idelay_ce; + +detect_alignment_proc: process(clk) + begin + ------------------------------------------------------------- + -- If there are a dozen or so symbol errors in at a rate of + -- greater than 1 in a million then advance the delay and + -- if that wraps then assert the bitslip signal + ------------------------------------------------------------- + if rising_edge(clk) then + ----------------------------------- + -- See if an error has been seen + -- + -- Holdoff gives a few cycles for + -- bitslips and delay changes to + -- take effect. + ----------------------------------- + error_seen <= '0'; + if holdoff = 0 then + if invalid_symbol = '1' then + error_seen <= '1'; + end if; + else + holdoff <= holdoff-1; + end if; + --------------------------------------------- + -- Keep track of valid symbol count vs errors + -- + -- Each error increase the count by a million, + -- each valid sysmbol decreases the count by + -- one. So after 12 errors it will cause us to + -- change bitslip or delay settings, but it will + -- take 7 million cycles until the high four + -- bits are zeros (and the link considered OK) + ----------------------------------------------- + bitslip <= '0'; + idelay_ce <= '0'; + if error_seen = '1' then + if signal_quality(27 downto 24) = x"F" then + ------------------------------------------ + -- Enough errors to cause us to loose sync + -- (if we had it!) + ------------------------------------------ + symbol_sync_i <= '0'; + -------------------------------------- + -- Hold off acting on any more errors + -- while we adjust the delay or bitslip + -------------------------------------- + holdoff <= (others => '1'); + ----------------------- + -- Bitslip if required + ----------------------- + if unsigned(idelay_count) = 31 then + bitslip <= '1'; + end if; + ------------------------------------------------------------------- + -- And adjust the delay setting (will wrap to 0 when bitslipping) + ------------------------------------------------------------------- + idelay_count <= std_logic_vector(unsigned(idelay_count)+1); + idelay_ce <= '1'; + ------------------------------------------------------------------- + -- It will need 4M good symbols to avoid adjusting the timing again + ------------------------------------------------------------------- + signal_quality(27 downto 24) <= x"4"; + else + signal_quality <= signal_quality + x"100000"; -- add a million if there is a symbol error + end if; + else + ----------------------------------------------- + -- Count down by one, as we are one symbol + -- closer to having a valid stream + ----------------------------------------------- + if signal_quality(27 downto 24) > 0 then + signal_quality <= signal_quality - 1; -- add a million if there is a symvole error; + end if; + end if; + ------------------------------------ + -- if we have counted down about 3M + -- symbols without any symbol errors + -- being seen then we are in sync + ------------------------------------ + if signal_quality(27 downto 24) = "0000" then + symbol_sync <= '1'; + end if; + end if; + end process; + +end Behavioral; diff --git a/src/audio_meters.vhd b/src/audio_meters.vhd new file mode 100644 index 0000000..a5c5588 --- /dev/null +++ b/src/audio_meters.vhd @@ -0,0 +1,257 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: audio_meters - Behavioral +-- +-- Description: Insert audio level meters on a video stream. +-- +-- Will need to make allowances for interlaced sources! +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity audio_meters is + Port ( clk : in STD_LOGIC; + ------------------------------- + -- VGA data recovered from HDMI + ------------------------------- + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_red : in std_logic_vector(7 downto 0); + in_green : in std_logic_vector(7 downto 0); + in_blue : in std_logic_vector(7 downto 0); + + ----------------------------------- + -- VGA data to be converted to HDMI + ----------------------------------- + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_red : out std_logic_vector(7 downto 0); + out_green : out std_logic_vector(7 downto 0); + out_blue : out std_logic_vector(7 downto 0); + + ------------------------------------- + -- Audio Levels + ------------------------------------- + signal audio_channel : in std_logic_vector(2 downto 0); + signal audio_de : in std_logic; + signal audio_level : in std_logic_vector(5 downto 0) + ); +end audio_meters; + +architecture Behavioral of audio_meters is + signal col_count : unsigned(11 downto 0); + signal line_count : unsigned(11 downto 0); + signal last_hsync : std_logic := '0'; + signal last_vsync : std_logic := '0'; + signal last_blank : std_logic := '0'; + + signal mid_blank : std_logic; + signal mid_hsync : std_logic; + signal mid_vsync : std_logic; + signal mid_red : std_logic_vector(7 downto 0); + signal mid_green : std_logic_vector(7 downto 0); + signal mid_blue : std_logic_vector(7 downto 0); + signal bar_draw : std_logic; + signal bar_col : unsigned(6 downto 0); -- 0-127 + signal bar_line : unsigned(5 downto 0); -- 0-63 + + type a_level is array (0 to 7) of unsigned(5 downto 0); + signal levels : a_level; + + type a_peak is array (0 to 7) of unsigned(7 downto 0); + signal peaks : a_peak; + + signal pending_drop : std_logic := '0'; + signal drop_index : unsigned(2 downto 0) := (others => '0'); + + signal u_sample : unsigned(5 downto 0) := (others => '0'); + signal level : unsigned(5 downto 0); + signal peak : unsigned(5 downto 0); + +begin + +level_proc: process(clk) + begin + if rising_edge(clk) then + ------------------------------------------------- + -- Update the peak level, or if pending_drop is + -- set then drop the peak and level by 1 every + -- frame. + -- + -- This causes 'peak' to fall at 1/4th the speed + -- of 'level', but makes for inconsistent + -- behaviour depending on frame rate :-( + ------------------------------------------------- + if audio_de = '1' then + if levels(to_integer(unsigned(audio_channel))) < unsigned(audio_level) then + levels(to_integer(unsigned(audio_channel))) <= unsigned(audio_level); + end if; + if peaks(to_integer(unsigned(audio_channel))) < unsigned(audio_level &"00") then + peaks(to_integer(unsigned(audio_channel))) <= unsigned(audio_level & "00"); + end if; + else + if pending_drop = '1' then + if levels(to_integer(drop_index)) > 0 then + levels(to_integer(drop_index)) <= levels(to_integer(drop_index))-1; + end if; + if peaks(to_integer(drop_index)) > 0 then + peaks(to_integer(drop_index)) <= peaks(to_integer(drop_index))-1; + end if; + if drop_index = "000" then + pending_drop <= '0'; + end if; + drop_index <= drop_index-1; + end if; + end if; + + -- Drop the levels once each frame + if last_vsync = '0' and in_vsync = '1' then + pending_drop <= '1'; + drop_index <= (others => '1'); + end if; + end if; + end process; + +video_proc: process(clk) + begin + if rising_edge(clk) then + out_blank <= mid_blank; + out_hsync <= mid_hsync; + out_vsync <= mid_vsync; + out_red <= mid_red; + out_green <= mid_green; + out_blue <= mid_blue; + + if bar_draw = '1' then + if bar_col(3 downto 1) /= "000" and bar_col(3 downto 1) /= "111" then + if peak > bar_line then + if peak > 60 then + out_red(out_red'high) <= '1'; + else + out_green(out_green'high) <= '1'; + end if; + end if; + + if level = bar_line then + out_red <= (others => '1'); + out_green <= (others => '1'); + out_blue <= (others => '1'); + end if; + end if; + end if; + + ----------------------------------------------------------------------------- + -- the mid_* signals contain the video with the box drawn to house the meters + ----------------------------------------------------------------------------- + mid_blank <= in_blank; + mid_hsync <= in_hsync; + mid_vsync <= in_vsync; + mid_red <= in_red; + mid_green <= in_green; + mid_blue <= in_blue; + -------------------------------------------------- + -- For working out if we need to draw colour bars + -------------------------------------------------- + bar_draw <= '0'; + bar_col <= unsigned(col_count(6 downto 0))-1; + bar_line <= to_unsigned(64,6)-unsigned(line_count(5 downto 0)); + + ----------------------------------------------------------------------------- + -- Retreive the levels for the bar. There is an + -- off-by-one error hidden by the bar boarder. + ----------------------------------------------------------------------------- + level <= levels(to_integer(col_count(6 downto 4))); + peak <= peaks(to_integer(col_count(6 downto 4)))(7 downto 2); + + ------------------------------------------------------- + -- Halve the intensity of the area where the meters are. + ------------------------------------------------------- + if col_count > 0 and col_count < 129 and line_count > 0 and line_count < 65 then + bar_draw <= '1'; + end if; + + if col_count > 0 and col_count < 129 and line_count > 0 and line_count < 65 then + mid_red <= "0" & in_red(in_red'high downto 1); + mid_green <= "0" & in_green(in_green'high downto 1); + mid_blue <= "0" & in_blue(in_blue'high downto 1); + end if; + + -- Draw bounding box left/right sides + if (col_count = 0 or col_count = 129) and line_count < 66 then + mid_red <= (others => '1'); + mid_green <= (others => '1'); + mid_blue <= (others => '1'); + end if; + -- Draw bounding box top/bottom sides + if (line_count = 0 or line_count = 65) and col_count < 130 then + mid_red <= (others => '1'); + mid_green <= (others => '1'); + mid_blue <= (others => '1'); + end if; + + + -- Increment the column count on when active pixels are seen + if in_blank = '0' then + col_count <= col_count + 1; + end if; + + -- The end of active video is used to increment the line count + if last_blank = '0' and in_blank = '1' then + line_count <= line_count + 1; + col_count <= (others => '0'); + end if; + + -- Reset the line count on falling vsync + if last_vsync = '0' and in_vsync = '1' then + line_count <= (others => '0'); + end if; + -- remember the hsync and vsync values + last_vsync <= in_vsync; + last_hsync <= in_hsync; + last_blank <= in_blank; + end if; + end process; +end Behavioral; diff --git a/src/audio_to_db.vhd b/src/audio_to_db.vhd new file mode 100644 index 0000000..b8bbb37 --- /dev/null +++ b/src/audio_to_db.vhd @@ -0,0 +1,194 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: audio_to_db - Behavioral +-- +-- Description: Calcuate the approximate DB level of an audio signal, with a +-- return of 63 indicating 0db, (e.g. 3 = -60fb) +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity audio_to_db is + Port ( clk : in STD_LOGIC; + + in_channel : in STD_LOGIC_VECTOR (2 downto 0); + in_de : in STD_LOGIC; + in_sample : in STD_LOGIC_VECTOR (23 downto 0); + + out_channel : out STD_LOGIC_VECTOR (2 downto 0); + out_de : out STD_LOGIC; + out_level : out STD_LOGIC_VECTOR (5 downto 0)); +end audio_to_db; + +architecture Behavioral of audio_to_db is + + signal s7_sample : unsigned (23 downto 0); + signal s7_de : STD_LOGIC; + signal s7_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s7_level : unsigned( 7 downto 0); + + signal s6_sample : unsigned (23 downto 0); + signal s6_de : STD_LOGIC; + signal s6_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s6_level : unsigned( 7 downto 0); + + signal s5_sample : unsigned (23 downto 0); + signal s5_de : STD_LOGIC; + signal s5_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s5_level : unsigned( 7 downto 0); + + signal s4_sample : unsigned (23 downto 0); + signal s4_de : STD_LOGIC; + signal s4_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s4_level : unsigned( 7 downto 0); + + signal s3_sample : unsigned (23 downto 0); + signal s3_de : STD_LOGIC; + signal s3_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s3_level : unsigned( 7 downto 0); + + signal s2_sample : unsigned (23 downto 0); + signal s2_de : STD_LOGIC; + signal s2_channel : STD_LOGIC_VECTOR (2 downto 0); + signal s2_level : unsigned( 7 downto 0); + + signal s1_sample : unsigned (23 downto 0); + signal s1_de : STD_LOGIC; + signal s1_channel : STD_LOGIC_VECTOR (2 downto 0); +begin + +process(clk) + begin + if rising_edge(clk) then + out_channel <= s7_channel; + out_de <= s7_de; + if s7_level(7 downto 6) = "00" then + out_level <= std_logic_vector(to_unsigned(63,6)-s7_level(5 downto 0)); + else + out_level <= (others => '0'); + end if; + + -- Finally the last stage to get a db level + s7_channel <= s6_channel; + s7_de <= s6_de; + if s6_sample(22 downto 15) < 72 then + s7_level <= s6_level + 5; + elsif s6_sample(22 downto 15) < 81 then + s7_level <= s6_level + 4; + elsif s6_sample(22 downto 15) < 91 then + s7_level <= s6_level + 3; + elsif s6_sample(22 downto 15) < 102 then + s7_level <= s6_level + 2; + elsif s6_sample(22 downto 15) < 114 then + s7_level <= s6_level + 1; + else + s7_level <= s6_level + 1; + end if; + + -- Stage 5 - shift up 2 bits if needed(bit 23 of sample will be 0) + s6_channel <= s5_channel; + s6_de <= s5_de; + if s5_sample(23 downto 22) = "00" then + s6_sample <= s5_sample(22 downto 0) & "0"; + s6_level <= s5_level + to_unsigned(6,8); + else + s6_sample <= s5_sample; + s6_level <= s5_level; + end if; + + -- Stage 5 - shift up 2 bits if needed(bit 23 of sample will be 0) + s5_channel <= s4_channel; + s5_de <= s4_de; + if s4_sample(23 downto 21) = "000" then + s5_sample <= s4_sample(21 downto 0) & "00"; + s5_level <= s4_level + to_unsigned(12,8); + else + s5_sample <= s4_sample; + s5_level <= s4_level; + end if; + + -- Stage 4 - shift up 4 bits if needed(bit 23 of sample will be 0) + s4_channel <= s3_channel; + s4_de <= s3_de; + if s3_sample(23 downto 19) = "00000" then + s4_sample <= s3_sample(19 downto 0) & "0000"; + s4_level <= s3_level + to_unsigned(24,8); + else + s4_sample <= s3_sample; + s4_level <= s3_level; + end if; + + -- Stage 3 - shift up 4 bits if needed(bit 23 of sample will be 0) + s3_channel <= s2_channel; + s3_de <= s2_de; + if s2_sample(23 downto 19) = "00000" then + s3_sample <= s2_sample(19 downto 0) & "0000"; + s3_level <= s2_level + to_unsigned(24,8); + else + s3_sample <= s2_sample; + s3_level <= s2_level; + end if; + + -- Stage 2 - shift up 4 bits if needed(bit 23 of sample will be 0) + s2_channel <= s1_channel; + s2_de <= s1_de; + if s1_sample(23 downto 19) = "00000" then + s2_sample <= s1_sample(19 downto 0) & "0000"; + s2_level <= to_unsigned(24,8); + else + s2_sample <= s1_sample; + s2_level <= to_unsigned(0,8); + end if; + + --- Stage 1 - remove any sign. + s1_channel <= in_channel; + s1_de <= in_de; + if in_sample(23) = '1' then + s1_sample <= to_unsigned(0,24) - unsigned(in_sample); + else + s1_sample <= unsigned(in_sample); + end if; + end if; + end process; + +end Behavioral; diff --git a/src/conversion_to_RGB.vhd b/src/conversion_to_RGB.vhd new file mode 100644 index 0000000..1b0fc45 --- /dev/null +++ b/src/conversion_to_RGB.vhd @@ -0,0 +1,226 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: conversion_YCbCr_to_RGB - Behavioral +-- +-- Description: Convert from RGB, studio level RGB or YCbCr to full range RGB +-- +-- Designed to take the same amount of time regardless of conversion +-- being performed. +---------------------------------------------------------------------------------- +-- When using 12-bit studio range inputs and the HD colourspace +-- +-- R = (Y-64)*1.164 + (Cb-2048) *0.090 + (Cr-2048)*1.793 +-- G = (Y-64)*1.164 - (Cb-2048) *0.213 - (Cr-2048)*0.533 +-- B = (Y-64)*1.164 + (Cb-2048) *2.112 + (Cr-2048)*0.000 +-- +-- To avoid the problems with signed/unsigned multiplication this +-- has been rearranged to +-- +-- R = Y*1.164 + Cb*0.090 + Cr*1.793 - 64*1.164 - 2048*0.090 - 2048*1.793 +-- G = Y*1.164 - Cb*0.213 - Cr*0.533 - 64*1.164 + 2048*0.213 + 2048*0.533 +-- B = Y*1.164 + Cb*2.112 + Cr*0.000 - 64*1.164 - 2048*2.112 - 2048*0.000 +-- +-- And then all the decimals have been scaled by 4096 This then only requires +-- five multipliers (as two are zero and three others are identical. +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity conversion_to_RGB is + port ( clk : in std_Logic; + input_is_YCbCr : in std_Logic; + input_is_sRGB : in std_Logic; + + ------------------------ + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_U : in std_logic_vector(11 downto 0); -- B or Cb + in_V : in std_logic_vector(11 downto 0); -- G or Y + in_W : in std_logic_vector(11 downto 0); -- R or Cr + + ------------------------ + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_R : out std_logic_vector(11 downto 0); + out_G : out std_logic_vector(11 downto 0); + out_B : out std_logic_vector(11 downto 0)); +end entity; + +architecture Behavioral of conversion_to_RGB is + ------------------------------ + -- For the pipeline + ------------------------------ + signal s1_blank : std_logic; + signal s1_hsync : std_logic; + signal s1_vsync : std_logic; + signal s1_U : std_logic_vector(12 downto 0); -- B or Cb, plus underflow guard bit + signal s1_V : std_logic_vector(12 downto 0); -- G or Y, plus underflow guard bit + signal s1_W : std_logic_vector(12 downto 0); -- R or Cr, plus underflow guard bit + + signal s2_blank : std_logic; + signal s2_hsync : std_logic; + signal s2_vsync : std_logic; + signal s2_U : std_logic_vector(12 downto 0); -- B or Cb, plus overflow guard bit + signal s2_V : std_logic_vector(12 downto 0); -- G or Y, plus overflow guard bit + signal s2_W : std_logic_vector(12 downto 0); -- R or Cr, plus overflow guard bit + + ------------------------------ + -- For Calculation + ------------------------------ + signal a : unsigned(26 downto 0) := (others => '0'); + signal b : unsigned(26 downto 0) := (others => '0'); + signal c : unsigned(26 downto 0) := (others => '0'); + signal d : unsigned(26 downto 0) := (others => '0'); + signal e : unsigned(26 downto 0) := (others => '0'); + signal R_raw : unsigned(26 downto 0) := (others => '0'); + signal G_raw : unsigned(26 downto 0) := (others => '0'); + signal B_raw : unsigned(26 downto 0) := (others => '0'); +begin +clk_proc: process(clk) + begin + if rising_edge(clk) then + ----------------------------------------------- + -- Step 3: clamp the result + ----------------------------------------------- + out_blank <= s2_blank; + out_hsync <= s2_hsync; + out_vsync <= s2_vsync; + if input_is_YCbCr = '0' then + -- trap overflows form prior stage + if s2_U(s2_U'high) = '0' then + out_B <= s2_U(s2_U'high-1 downto 0); + else + out_B <= (others => '1'); + end if; + + if s2_V(s2_V'high) = '0' then + out_G <= s2_V(s2_V'high-1 downto 0); + else + out_G <= (others => '1'); + end if; + + if s2_W(s2_W'high) = '0' then + out_R <= s2_W(s2_W'high-1 downto 0); + else + out_R <= (others => '1'); + end if; + else + case R_raw(R_raw'high-1 downto R_raw'high-2) is + when "00" => out_R <= std_logic_vector(R_raw(R_raw'high-3 downto R_raw'high-14)); -- In range + when "01" => out_R <= (others => '1'); -- Overflow + when others => out_R <= (others => '0'); -- Underflow + end case; + + case G_raw(G_raw'high-1 downto G_raw'high-2) is + when "00" => out_G <= std_logic_vector(G_raw(G_raw'high-3 downto G_raw'high-14)); -- In range + when "01" => out_G <= (others => '1'); -- Overflow + when others => out_G <= (others => '0'); -- Underflow + end case; + + case B_raw(B_raw'high-1 downto B_raw'high-2) is + when "00" => out_B <= std_logic_vector(B_raw(B_raw'high-3 downto B_raw'high-14)); -- In range + when "01" => out_B <= (others => '1'); -- Overflow + when others => out_B <= (others => '0'); -- Underflow + end case; + end if; + ------------------------------------------------- + -- Step 2: Add the partial results and remove the + -- offset introduced by the use of studio range + ------------------------------------------------- + s2_blank <= s1_blank; + s2_hsync <= s1_hsync; + s2_vsync <= s1_vsync; + if input_is_sRGB = '1' then + -- Trap underflows from prior stage + if s1_U(s1_U'high) = '0' then + s2_U <= std_logic_vector(unsigned(s1_U) + unsigned(s1_U(s1_U'high downto 5))); + else + s2_U <= (others => '0'); + end if; + + if s1_V(s1_V'high) = '0' then + s2_V <= std_logic_vector(unsigned(s1_V) + unsigned(s1_V(s1_V'high downto 5))); + else + s2_V <= (others => '0'); + end if; + + if s1_W(s1_W'high) = '0' then + s2_W <= std_logic_vector(unsigned(s1_W) + unsigned(s1_W(s1_W'high downto 5))); + else + s2_W <= (others => '0'); + end if; + else + s2_U <= s1_U; + s2_V <= s1_V; + s2_W <= s1_W; + end if; + R_raw <= a + d - to_unsigned(4767*256 + 0*2048 + 7344*2048, 27); + G_raw <= a - b - e - to_unsigned(4767*256 - 872*2048 - 2183*2048, 27); + B_raw <= a + c - to_unsigned(4767*256 + 8650*2048 + 0*2048, 27); + + ------------------------------------------------- + -- Step 1: Multiply the incoming values by the + -- Conversion coefficients + ------------------------------------------------- + s1_blank <= in_blank; + s1_hsync <= in_hsync; + s1_vsync <= in_vsync; + if input_is_sRGB = '1' then + s1_U <= std_logic_vector(unsigned('0' & in_U) - 256); + s1_V <= std_logic_vector(unsigned('0' & in_V) - 256); + s1_W <= std_logic_vector(unsigned('0' & in_W) - 256); + else + s1_U <= '0' & in_U; + s1_V <= '0' & in_V; + s1_W <= '0' & in_W; + end if; + a <= unsigned(in_V) * to_unsigned(4767,15); -- 1.164 * 2^12 + b <= unsigned(in_U) * to_unsigned( 872,15); -- 0.213 * 2^12 + c <= unsigned(in_U) * to_unsigned(8650,15); -- 2.112 * 2^12 + d <= unsigned(in_W) * to_unsigned(7344,15); -- 1.793 * 2^12 + e <= unsigned(in_W) * to_unsigned(2183,15); -- 0.533 * 2^12 + end if; + end process; +end architecture; diff --git a/src/deserialiser_1_to_10.vhd b/src/deserialiser_1_to_10.vhd new file mode 100644 index 0000000..3047ffb --- /dev/null +++ b/src/deserialiser_1_to_10.vhd @@ -0,0 +1,178 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: deserialiser_1_to_10 - Behavioral +-- +-- Description: A 10-to-1 deserialiser for the Artix 7 +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.std_logic_1164.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +entity deserialiser_1_to_10 is + Port ( clk_mgmt : in std_logic; + delay_ce : in std_logic; + delay_count : in std_logic_vector (4 downto 0); + + ce : in STD_LOGIC; + clk : in std_logic; + clk_x1 : in std_logic; + bitslip : in std_logic; + clk_x5 : in std_logic; + serial : in std_logic; + reset : in std_logic; + data : out std_logic_vector (9 downto 0)); +end deserialiser_1_to_10; + +architecture Behavioral of deserialiser_1_to_10 is + signal delayed : std_logic := '0'; + signal shift1 : std_logic := '0'; + signal shift2 : std_logic := '0'; + signal clkb : std_logic := '1'; + attribute IODELAY_GROUP : STRING; + attribute IODELAY_GROUP of IDELAYE2_inst: label is "idelay_group"; + +begin + +IDELAYE2_inst : IDELAYE2 + generic map ( + CINVCTRL_SEL => "FALSE", + DELAY_SRC => "DATAIN", + HIGH_PERFORMANCE_MODE => "TRUE", + IDELAY_TYPE => "VAR_LOAD", + IDELAY_VALUE => 0, + PIPE_SEL => "FALSE", + REFCLK_FREQUENCY => 200.0, + SIGNAL_PATTERN => "DATA" + ) + port map ( + DATAIN => serial, + IDATAIN => '0', + DATAOUT => delayed, + -- + CNTVALUEOUT => open, + C => clk, + CE => delay_ce, + CINVCTRL => '0', + CNTVALUEIN => delay_count, + INC => '0', + LD => '1', + LDPIPEEN => '0', + REGRST => '0' + ); + clkb <= not clk_x5; + +ISERDESE2_master : ISERDESE2 + generic map ( + DATA_RATE => "DDR", + DATA_WIDTH => 10, + DYN_CLKDIV_INV_EN => "FALSE", + DYN_CLK_INV_EN => "FALSE", + INIT_Q1 => '0', INIT_Q2 => '0', INIT_Q3 => '0', INIT_Q4 => '0', + INTERFACE_TYPE => "NETWORKING", + IOBDELAY => "IFD", + NUM_CE => 1, + OFB_USED => "FALSE", + SERDES_MODE => "MASTER", + SRVAL_Q1 => '0', SRVAL_Q2 => '0', SRVAL_Q3 => '0', SRVAL_Q4 => '0' + ) + port map ( + O => open, + Q1 => data(9), Q2 => data(8), Q3 => data(7), Q4 => data(6), + Q5 => data(5), Q6 => data(4), Q7 => data(3), Q8 => data(2), + SHIFTOUT1 => shift1, SHIFTOUT2 => shift2, + BITSLIP => bitslip, + CE1 => ce, CE2 => '1', + CLKDIVP => '0', + CLK => clk_x5, + CLKB => clkb, + CLKDIV => clk_x1, + OCLK => '0', + DYNCLKDIVSEL => '0', + DYNCLKSEL => '0', + D => '0', + DDLY => delayed, + OFB => '0', + OCLKB => '0', + RST => reset, + SHIFTIN1 => '0', + SHIFTIN2 => '0' + ); + +ISERDESE2_slave : ISERDESE2 + generic map ( + DATA_RATE => "DDR", + DATA_WIDTH => 10, + DYN_CLKDIV_INV_EN => "FALSE", + DYN_CLK_INV_EN => "FALSE", + INIT_Q1 => '0', INIT_Q2 => '0', INIT_Q3 => '0', INIT_Q4 => '0', + INTERFACE_TYPE => "NETWORKING", + IOBDELAY => "IFD", + NUM_CE => 1, + OFB_USED => "FALSE", + SERDES_MODE => "SLAVE", + SRVAL_Q1 => '0', SRVAL_Q2 => '0', SRVAL_Q3 => '0', SRVAL_Q4 => '0' + ) + port map ( + O => open, + Q1 => open, Q2 => open, Q3 => data(1), Q4 => data(0), + Q5 => open, Q6 => open, Q7 => open, Q8 => open, + SHIFTOUT1 => open, SHIFTOUT2 => open, + BITSLIP => bitslip, + CE1 => ce, CE2 => '1', + CLKDIVP => '0', + CLK => CLK_x5, + CLKB => clkb, + CLKDIV => clk_x1, + OCLK => '0', + DYNCLKDIVSEL => '0', + DYNCLKSEL => '0', + D => '0', + DDLY => '0', + OFB => '0', + OCLKB => '0', + RST => reset, + SHIFTIN1 => shift1, + SHIFTIN2 => shift2 + ); +end Behavioral; \ No newline at end of file diff --git a/src/dvid_output.vhd b/src/dvid_output.vhd new file mode 100644 index 0000000..8c01511 --- /dev/null +++ b/src/dvid_output.vhd @@ -0,0 +1,168 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: DVID_output - Behavioral +-- +-- Description: Convert a stream of pixels into a DVID output +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.std_logic_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +entity DVID_output is + Port ( + pixel_clk : in std_logic; -- Driven by BUFG + pixel_io_clk_x1 : in std_logic; -- Driven by BUFIO + pixel_io_clk_x5 : in std_logic; -- Driven by BUFIO + + -- VGA Signals + vga_blank : in std_logic; + vga_hsync : in std_logic; + vga_vsync : in std_logic; + vga_red : in std_logic_vector(7 downto 0); + vga_blue : in std_logic_vector(7 downto 0); + vga_green : in std_logic_vector(7 downto 0); + data_valid : in std_logic; + + --- DVI-D out + tmds_out_clk : out std_logic; + tmds_out_ch0 : out std_logic; + tmds_out_ch1 : out std_logic; + tmds_out_ch2 : out std_logic + ); +end DVID_output; + +architecture Behavioral of DVID_output is + + component tmds_encoder is + Port ( clk : in std_logic; + data : in std_logic_vector (7 downto 0); + c : in std_logic_vector (1 downto 0); + blank : in std_logic; + encoded : out std_logic_vector (9 downto 0)); + end component; + + component serialiser_10_to_1 is + Port ( clk : in std_logic; + clk_x5 : in std_logic; + reset : in std_logic; + data : in std_logic_vector (9 downto 0); + serial : out std_logic); + end component; + + signal c0_tmds_symbol : std_logic_vector (9 downto 0); + signal c1_tmds_symbol : std_logic_vector (9 downto 0); + signal c2_tmds_symbol : std_logic_vector (9 downto 0); + + signal reset_sr : std_logic_vector (2 downto 0) := (others => '1'); + signal reset : std_logic := '1'; + +begin + reset <= reset_sr(0); + +process(pixel_clk, data_valid) + begin + if data_valid = '0' then + reset_sr <= (others => '1'); + elsif rising_edge(pixel_clk) then + reset_sr <= '0' & reset_sr(reset_sr'high downto 1); + end if; + end process; + --------------------- + -- TMDS Encoders + --------------------- +c0_tmds: tmds_encoder port map ( + clk => pixel_clk, + data => vga_blue, + c(1) => vga_vsync, + c(0) => vga_hsync, + blank => vga_blank, + encoded => c0_tmds_symbol); + +c1_tmds: tmds_encoder port map ( + clk => pixel_clk, + data => vga_green, + c => (others => '0'), + blank => vga_blank, + encoded => c1_tmds_symbol); + +c2_tmds: tmds_encoder port map ( + clk => pixel_clk, + data => vga_red, + c => (others => '0'), + blank => vga_blank, + encoded => c2_tmds_symbol); + --------------------- + -- Output serializers + --------------------- +ser_ch0: serialiser_10_to_1 port map ( + clk => pixel_io_clk_x1, + clk_x5 => pixel_io_clk_x5, + reset => reset, + data => c0_tmds_symbol, + serial => tmds_out_ch0); + +ser_ch1: serialiser_10_to_1 port map ( + clk => pixel_io_clk_x1, + clk_x5 => pixel_io_clk_x5, + reset => reset, + data => c1_tmds_symbol, + serial => tmds_out_ch1); + +ser_ch2: serialiser_10_to_1 port map ( + clk => pixel_io_clk_x1, + clk_x5 => pixel_io_clk_x5, + reset => reset, + data => c2_tmds_symbol, + serial => tmds_out_ch2); + +ser_clk: serialiser_10_to_1 Port map ( + clk => pixel_io_clk_x1, + clk_x5 => pixel_io_clk_x5, + reset => reset, + data => "0000011111", + serial => tmds_out_clk); + +end Behavioral; diff --git a/src/edid_rom.vhd b/src/edid_rom.vhd new file mode 100644 index 0000000..fb14e27 --- /dev/null +++ b/src/edid_rom.vhd @@ -0,0 +1,363 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: edid_rom - Behavioral +-- +-- Description: A simple EDID ROM, configured for 1920x1080@60Hz, HDMI format. +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +----------------------------------------------------------------------------------library ieee; +use ieee.std_logic_1164.all; +use ieee.numeric_std.all; + +library UNISIM; +use UNISIM.VComponents.all; + +entity edid_rom is + port ( clk : in std_logic; + sclk_raw : in std_logic; + sdat_raw : inout std_logic := 'Z'; + edid_debug : out std_logic_vector(2 downto 0) := (others => '0') + ); +end entity; + +architecture Behavioral of edid_rom is + + type a_edid_rom is array (0 to 255) of std_logic_vector(7 downto 0); + + signal edid_rom : a_edid_rom := ( + ------- BASE EDID Bytes 0 to 35 ----------------------------- + -- Header + x"00",x"FF",x"FF",x"FF",x"FF",x"FF",x"FF",x"00", + -- EISA ID - Manufacturer, Product, + x"04",x"43", x"07",x"f2", + -- EISA ID -Serial + x"01",x"00",x"00",x"00", + -- Model/year + x"FF", x"11", + -- EDID Version + x"01", x"04", + ------------------------------------ + ------------------------------------ + -- Digital Video using DVI, 8 bits + --- x"81", -- Checksum 0xB6 + ------------------------------------ + -- Digital Video using HDMI, 8 bits + x"A2", -- Checksum 0x95 + ------------------------------------ + -- Aspect ratio, flag, gamma + x"4f", x"00", x"78", + ------------------------------------ + -- Features + x"3E", + -- Display x,y Chromaticity V Breaks here! + x"EE", x"91", x"a3", x"54", x"4c", x"99", x"26", x"0f", x"50", x"54", + -- Established timings + x"20", x"00", x"00", + -- Standard timings + x"01", x"01", x"01", x"01", x"01", x"01", x"01", x"01", + x"01", x"01", x"01", x"01", x"01", x"01", x"01", x"01", + ------- End of BASE EDID --------------------------------- + + ----- 18 byte data block 1080p -------- + -- Pixel clock + x"02",x"3A", + -- Horizontal 1920 with 280 blanking + x"80", x"18", x"71", + -- Vertical 1080 with 45 lines blanking + x"38", x"2D", x"40", + -- Horizontal front porch + x"58",x"2C", + -- Vertical front porch + x"04",x"05", + -- Horizontal and vertical image size + x"0f", x"48", x"42", + -- Horizontal and vertical boarder + x"00", x"00", + -- Options (non-interlaces, not 3D, syncs...) + x"1E", + + ----- 18 byte data block 1080i -------- + -- Pixel clock + x"01",x"1D", + -- Horizontal 1920 with 280 blanking + x"80", x"18", x"71", + -- Vertical 1080 with 45 lines blanking + x"1C", x"16", x"20", + -- Horizontal front porch + x"58",x"2C", + -- Vertical front porch -- SEEMS WRONG! + x"25",x"00", + -- Horizontal and vertical image size + x"0f", x"48", x"42", + -- Horizontal and vertical boarder + x"00", x"00", + -- Options (non-interlaces, not 3D, syncs...) + x"9E", + + ----- 18 byte data block 720p -------- + -- Pixel clock + x"01",x"1D", + -- Horizontal 1920 with 280 blanking + x"00", x"72", x"51", + -- Vertical 1080 with 45 lines blanking + x"D0", x"1E", x"20", + -- Horizontal front porch + x"6E",x"28", + -- Vertical front porch -- SEEMS WRONG! + x"55",x"00", + -- Horizontal and vertical image size + x"0f", x"48", x"42", + -- Horizontal and vertical boarder + x"00", x"00", + -- Options (non-interlaces, not 3D, syncs...) + x"1E", + + ----- 18 byte data block 720p -------- + -- Monitor name ASCII descriptor + x"00", x"00", x"00", x"FC", x"00", + -- ASCII name - "ABC LCD47w[lf] " + x"48", x"61", x"6D", x"73", x"74", x"65", x"72", x"6B", + x"73", x"0A", x"20", x"20", x"20", + + ----- End of EDID block + -- Extension flag & checksum + x"01", x"74", + + x"02", x"03", x"18", x"72", x"47", x"90", x"85", x"04", x"03", x"02", x"07", x"06", x"23", x"09", x"07", x"07", + x"83", x"01", x"00", x"00", x"65", x"03", x"0C", x"00", x"10", x"00", x"8E", x"0A", x"D0", x"8A", x"20", x"E0", + x"2d", x"10", x"10", x"3E", x"96", x"00", x"1F", x"09", x"00", x"00", x"00", x"18", x"8E", x"0A", x"D0", x"8A", + x"20", x"E0", x"2D", x"10", x"10", x"3E", x"96", x"00", x"04", x"03", x"00", x"00", x"00", x"18", x"8E", x"0A", + x"A0", x"14", x"51", x"F0", x"16", x"00", x"26", x"7C", x"43", x"00", x"1F", x"09", x"00", x"00", x"00", x"98", + x"8E", x"0A", x"A0", x"14", x"51", x"F0", x"16", x"00", x"26", x"7C", x"43", x"00", x"04", x"03", x"00", x"00", + x"00", x"98", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", + x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"00", x"C9" + + ); + + signal sclk_delay : std_logic_vector(2 downto 0); + signal sdat_delay : unsigned(6 downto 0); + + type t_state is ( state_idle, + -- States to support writing the device's address + state_start, + state_dev7, + state_dev6, + state_dev5, + state_dev4, + state_dev3, + state_dev2, + state_dev1, + state_dev0, + -- States to support writing the address + state_ack_device_write, + state_addr7, + state_addr6, + state_addr5, + state_addr4, + state_addr3, + state_addr2, + state_addr1, + state_addr0, + state_addr_ack, + -- States to support the selector device + state_selector_ack_device_write, + state_selector_addr7, + state_selector_addr6, + state_selector_addr5, + state_selector_addr4, + state_selector_addr3, + state_selector_addr2, + state_selector_addr1, + state_selector_addr0, + state_selector_addr_ack, + -- States to support reading from the the EDID ROM + state_ack_device_read, + state_read7, + state_read6, + state_read5, + state_read4, + state_read3, + state_read2, + state_read1, + state_read0, + state_read_ack); + + signal state : t_state := state_idle; + signal data_out_sr : std_logic_vector(7 downto 0) := (others => '1'); + signal data_shift_reg : std_logic_vector(7 downto 0) := (others => '0'); + signal addr_reg : unsigned(7 downto 0) := (others => '0'); + signal selector_reg : unsigned(7 downto 0) := (others => '0'); + signal data_to_send : std_logic_vector(7 downto 0) := (others => '0'); + signal data_out_delay : std_logic_vector(7 downto 0) := (others => '0'); + signal PULL_LOW : std_logic := '0'; + signal sdat_input : std_logic := '0'; + signal sdat_delay_last : std_logic := '0'; +begin + +i_IOBUF: IOBUF + generic map ( + DRIVE => 12, + IOSTANDARD => "DEFAULT", + SLEW => "SLOW") + port map ( + O => sdat_input, -- Buffer output + IO => sdat_raw, -- Buffer inout port (connect directly to top-level port) + I => '0', -- Buffer input + T => data_out_sr(data_out_sr'high) -- 3-state enable input, high=input, low=output + ); + edid_debug(0) <= std_logic(sdat_delay(sdat_delay'high)); + edid_debug(1) <= sclk_raw; + +process(clk) + begin + if rising_edge(clk) then + + -- falling edge on SDAT while sclk is held high = START condition + if sclk_delay(1) = '1' and sclk_delay(0) = '1' and sdat_delay_last = '1' and sdat_delay(sdat_delay'high) = '0' then + state <= state_start; + edid_debug(2) <= '1'; + end if; + + -- rising edge on SDAT while sclk is held high = STOP condition + if sclk_delay(1) = '1' and sclk_delay(0) = '1' and sdat_delay_last = '0' and sdat_delay(sdat_delay'high) = '1' then + state <= state_idle; + selector_reg <= (others => '0'); + edid_debug(2) <= '0'; + end if; + + -- rising edge on SCLK - usually a data bit + if sclk_delay(1) = '1' and sclk_delay(0) = '0' then + -- Move data into a shift register + data_shift_reg <= data_shift_reg(data_shift_reg'high-1 downto 0) & std_logic(sdat_delay(sdat_delay'high)); + end if; + + -- falling edge on SCLK - time to change state + if sclk_delay(1) = '0' and sclk_delay(0) = '1' then + data_out_sr <= data_out_sr(data_out_sr'high-1 downto 0) & '1'; -- Add Pull up + case state is + when state_start => state <= state_dev7; + when state_dev7 => state <= state_dev6; + when state_dev6 => state <= state_dev5; + when state_dev5 => state <= state_dev4; + when state_dev4 => state <= state_dev3; + when state_dev3 => state <= state_dev2; + when state_dev2 => state <= state_dev1; + when state_dev1 => state <= state_dev0; + when state_dev0 => if data_shift_reg = x"A1" then + state <= state_ack_device_read; + data_out_sr(data_out_sr'high) <= '0'; -- Send Slave ACK + elsif data_shift_reg = x"A0" then + state <= state_ack_device_write; + data_out_sr(data_out_sr'high) <= '0'; -- Send Slave ACK + elsif data_shift_reg = x"60" then + state <= state_selector_ack_device_write; + data_out_sr(data_out_sr'high) <= '0'; -- Send Slave ACK + else + state <= state_idle; + end if; + when state_ack_device_write => state <= state_addr7; + when state_addr7 => state <= state_addr6; + when state_addr6 => state <= state_addr5; + when state_addr5 => state <= state_addr4; + when state_addr4 => state <= state_addr3; + when state_addr3 => state <= state_addr2; + when state_addr2 => state <= state_addr1; + when state_addr1 => state <= state_addr0; + when state_addr0 => state <= state_addr_ack; + addr_reg <= unsigned(data_shift_reg); + data_out_sr(data_out_sr'high) <= '0'; -- Send Slave ACK + when state_addr_ack => state <= state_idle; -- SLave ACK and ignore any written data + ------------------------------------ + -- Process the write to the selector + ------------------------------------ + when state_selector_ack_device_write => state <= state_selector_addr7; + when state_selector_addr7 => state <= state_selector_addr6; + when state_selector_addr6 => state <= state_selector_addr5; + when state_selector_addr5 => state <= state_selector_addr4; + when state_selector_addr4 => state <= state_selector_addr3; + when state_selector_addr3 => state <= state_selector_addr2; + when state_selector_addr2 => state <= state_selector_addr1; + when state_selector_addr1 => state <= state_selector_addr0; + when state_selector_addr0 => state <= state_selector_addr_ack; + selector_reg <= unsigned(data_shift_reg(7 downto 0)); + data_out_sr(data_out_sr'high) <= '0'; -- Send Slave ACK + when state_selector_addr_ack => state <= state_idle; -- SLave ACK and ignore any written data + ------------------------- + + when state_ack_device_read => state <= state_read7; + data_out_sr <= edid_rom(to_integer(addr_reg)); + when state_read7 => state <= state_read6; + when state_read6 => state <= state_read5; + when state_read5 => state <= state_read4; + when state_read4 => state <= state_read3; + when state_read3 => state <= state_read2; + when state_read2 => state <= state_read1; + when state_read1 => state <= state_read0; + when state_read0 => state <= state_read_ack; + when state_read_ack => if sdat_delay(sdat_delay'high) = '0' then + state <= state_read7; + data_out_sr <= edid_rom(to_integer(addr_reg+1)); + else + state <= state_idle; + end if; + addr_reg <= addr_reg+1; + when others => state <= state_idle; + end case; + end if; + sdat_delay_last <= sdat_delay(sdat_delay'high); + -- Synchronisers for SCLK and SDAT + sclk_delay <= sclk_raw & sclk_delay(sclk_delay'high downto 1); + -- Resolve any 'Z' state in simulation - make it pull up. + if sdat_input = '0' then + if sdat_delay(sdat_delay'high) = '1' then + sdat_delay <= sdat_delay - 1; + else + sdat_delay <= (others => '0'); + end if; + else + if sdat_delay(sdat_delay'high) = '0' then + sdat_delay <= sdat_delay + 1; + else + sdat_delay <= (others => '1'); + end if; + end if; + end if; + end process; +end architecture; \ No newline at end of file diff --git a/src/expand_422_to_444.vhd b/src/expand_422_to_444.vhd new file mode 100644 index 0000000..c7bf1e6 --- /dev/null +++ b/src/expand_422_to_444.vhd @@ -0,0 +1,152 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: expand_422_to_444 - Behavioral +-- +-- Description: Convert incoming 422 data to 444 +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity expand_422_to_444 is + Port ( clk : in STD_LOGIC; + input_is_422 : in std_logic; + ------------------ + -- Incoming pixels + ------------------ + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_ch2 : in std_logic_vector(7 downto 0); + in_ch1 : in std_logic_vector(7 downto 0); + in_ch0 : in std_logic_vector(7 downto 0); + + ------------------- + -- Processed pixels + ------------------- + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_U : out std_logic_vector(11 downto 0); -- B or Cb + out_V : out std_logic_vector(11 downto 0); -- G or Y + out_W : out std_logic_vector(11 downto 0) -- R or Cr + ); +end expand_422_to_444; + +architecture Behavioral of expand_422_to_444 is + + signal in_blank_1 : std_logic := '0'; + signal in_hsync_1 : std_logic := '0'; + signal in_vsync_1 : std_logic := '0'; + signal in_ch0_1 : std_logic_vector(7 downto 0) := (others => '0'); + signal in_ch1_1 : std_logic_vector(7 downto 0) := (others => '0'); + signal in_ch2_1 : std_logic_vector(7 downto 0) := (others => '0'); + + signal in_blank_2 : std_logic := '0'; + signal in_hsync_2 : std_logic := '0'; + signal in_vsync_2 : std_logic := '0'; + signal in_ch0_2 : std_logic_vector(7 downto 0) := (others => '0'); + signal in_ch1_2 : std_logic_vector(7 downto 0) := (others => '0'); + signal in_ch2_2 : std_logic_vector(7 downto 0) := (others => '0'); + + signal first_of_pair : std_logic := '0'; +begin + +process(clk) + begin + if rising_edge(clk) then + if input_is_422 = '1' then + ------------------------------------------------------ + -- For 422, copy the chroma values between pixel pairs + ------------------------------------------------------ + out_blank <= in_blank_1; + out_hsync <= in_hsync_1; + out_vsync <= in_vsync_1; + if in_blank_1 = '1' then + first_of_pair <= '1'; + out_U <= in_ch2_1 & in_ch0_1(7 downto 4); -- Cb + out_V <= in_ch1_1 & in_ch0_1(3 downto 0); -- Y + out_W <= in_ch2_1 & in_ch0_1(7 downto 4); -- Cr + else + if first_of_pair = '1' then + -- Take Cr from the next pixel + first_of_pair <= '0'; + out_U <= in_ch2_1 & in_ch0_1(7 downto 4); -- Cb + out_V <= in_ch1_1 & in_ch0_1(3 downto 0); -- Y + out_W <= in_ch2 & in_ch0 (7 downto 4); -- Cr + else + -- Take Cb from the prior pixel + first_of_pair <= '1'; + out_U <= in_ch2_2 & in_ch0_2(7 downto 4); -- Cb + out_V <= in_ch1_1 & in_ch0_1(3 downto 0); -- Y + out_W <= in_ch2_1 & in_ch0_1(7 downto 4); -- Cr + end if; + end if; + else + ------------------------------------------------------ + -- Minimal processing for 422 (either RGB or YCbCr) + ------------------------------------------------------ + out_blank <= in_blank_1; + out_hsync <= in_hsync_1; + out_vsync <= in_vsync_1; + out_U <= in_ch0_1 & "0000"; -- B or Cb + out_V <= in_ch1_1 & "0000"; -- G or Y + out_W <= in_ch2_1 & "0000"; -- R or Cr + end if; + + -- Remember the pixel for two cycles + in_blank_1 <= in_blank; + in_hsync_1 <= in_hsync; + in_vsync_1 <= in_vsync; + in_ch0_1 <= in_ch0; + in_ch1_1 <= in_ch1; + in_ch2_1 <= in_ch2; + + in_blank_2 <= in_blank_1; + in_hsync_2 <= in_hsync_1; + in_vsync_2 <= in_vsync_1; + in_ch0_2 <= in_ch0_1; + in_ch1_2 <= in_ch1_1; + in_ch2_2 <= in_ch2_1; + end if; + end process; +end Behavioral; diff --git a/src/extract_audio_samples.vhd b/src/extract_audio_samples.vhd new file mode 100644 index 0000000..2a8df32 --- /dev/null +++ b/src/extract_audio_samples.vhd @@ -0,0 +1,111 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: extract_audio_samples - Behavioral +-- +-- Description: Extract audio data from the HDMI ADP data stream +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity extract_audio_samples is + Port ( clk : in STD_LOGIC; + adp_data_valid : in STD_LOGIC; + adp_header_bit : in STD_LOGIC; + adp_frame_bit : in STD_LOGIC; + adp_subpacket0_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket1_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket2_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket3_bits : in STD_LOGIC_VECTOR (1 downto 0); + audio_de : out STD_LOGIC; + audio_channel : out STD_LOGIC_VECTOR (2 downto 0); + audio_sample : out STD_LOGIC_VECTOR (23 downto 0) + ); +end extract_audio_samples; + +architecture Behavioral of extract_audio_samples is + signal header_bits : STD_LOGIC_VECTOR (31 downto 0); + signal frame_bits : STD_LOGIC_VECTOR (31 downto 0); + signal subpacket0_bits : STD_LOGIC_VECTOR (63 downto 0); + signal subpacket1_bits : STD_LOGIC_VECTOR (63 downto 0); + signal subpacket2_bits : STD_LOGIC_VECTOR (63 downto 0); + signal subpacket3_bits : STD_LOGIC_VECTOR (63 downto 0); + signal grab_other_channel : std_logic := '0'; +begin + +process(clk) + begin + if rising_edge(clk) then + ----------------------------------------------- + -- Move the incoming bits into a shift register + ----------------------------------------------- + header_bits <= adp_header_bit & header_bits(header_bits'high downto 1); + frame_bits <= (adp_frame_bit and adp_data_valid) & frame_bits(frame_bits'high downto 1); + subpacket0_bits <= adp_subpacket0_bits & subpacket0_bits(subpacket0_bits'high downto 2); + subpacket1_bits <= adp_subpacket1_bits & subpacket1_bits(subpacket1_bits'high downto 2); + subpacket2_bits <= adp_subpacket2_bits & subpacket2_bits(subpacket2_bits'high downto 2); + subpacket3_bits <= adp_subpacket3_bits & subpacket3_bits(subpacket3_bits'high downto 2); + + audio_de <= '0'; + + if grab_other_channel = '1' then + audio_de <= header_bits(7); + audio_channel <= "001"; + audio_sample <= subpacket0_bits(45 downto 22); + grab_other_channel <= '0'; + end if; + if frame_bits = x"FFFFFFFE" then + ------------------------------------------------ + -- Check the packet type as being audio samples + ------------------------------------------------ + if header_bits(7 downto 0) = x"02" then + audio_de <= header_bits(8); + audio_channel <= "000"; + audio_sample <= subpacket0_bits(23 downto 0); + grab_other_channel <= '1'; + end if; + end if; + end if; + end process; + +end Behavioral; diff --git a/src/extract_video_infopacket_data.vhd b/src/extract_video_infopacket_data.vhd new file mode 100644 index 0000000..75110bc --- /dev/null +++ b/src/extract_video_infopacket_data.vhd @@ -0,0 +1,115 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: extract_video_infopacket_data - Behavioral +-- +-- Description: Extract a couple of fields from the video infopacket, allowin use +-- to correctly convert the incoming pixels into RGB 444 for internal +-- processing. +-- +-- Bits 14:13 indicate the colour space and 444 vs 422. +-- Bits 27:26 indicate if the pixels are studio level (16-240) +-- or full range (0-255) +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity extract_video_infopacket_data is + Port ( clk : in STD_LOGIC; + adp_data_valid : in STD_LOGIC; + adp_header_bit : in STD_LOGIC; + adp_frame_bit : in STD_LOGIC; + adp_subpacket0_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket1_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket2_bits : in STD_LOGIC_VECTOR (1 downto 0); + adp_subpacket3_bits : in STD_LOGIC_VECTOR (1 downto 0); + input_is_YCbCr : out STD_LOGIC; + input_is_422 : out STD_LOGIC; + input_is_sRGB : out STD_LOGIC); +end extract_video_infopacket_data; + +architecture Behavioral of extract_video_infopacket_data is + -- For this usage, we are only interested in four bits that are all in the first + -- 16 transfers of the 32-bit packets + signal header_bits : STD_LOGIC_VECTOR (15 downto 0); + signal frame_bits : STD_LOGIC_VECTOR (15 downto 0); + signal subpacket0_bits : STD_LOGIC_VECTOR (31 downto 0); + signal updated : std_logic := '0'; +begin + +process(clk) + begin + if rising_edge(clk) then + if adp_data_valid = '1' then + ----------------------------------------------- + -- Move the incoming bits into a shift register + ----------------------------------------------- + header_bits <= adp_header_bit & header_bits(header_bits'high downto 1); + frame_bits <= adp_frame_bit & frame_bits(frame_bits'high downto 1); + subpacket0_bits <= adp_subpacket0_bits & subpacket0_bits(subpacket0_bits'high downto 2); + updated <= '1'; + end if; + + ---------------------------------------------------- + -- The 0 in frame bits indicates the start of packet + ---------------------------------------------------- + if updated = '1' and frame_bits = x"FFFE" then + -- 82 is the type of packet, 02 is the version + if header_bits = x"0282" then + case subpacket0_bits(14 downto 13) is + when "00" => input_is_YCbCr <= '0'; input_is_422 <= '0'; + when "01" => input_is_YCbCr <= '1'; input_is_422 <= '1'; + when "10" => input_is_YCbCr <= '1'; input_is_422 <= '0'; + when others => NULL; + end case; + + case subpacket0_bits(27 downto 26) is + when "01" => input_is_sRGB <= '1'; + when others => input_is_sRGB <= '0'; + end case; + + end if; + end if; + end if; + end process; + +end Behavioral; diff --git a/src/hdmi_design.vhd b/src/hdmi_design.vhd new file mode 100644 index 0000000..e1d9b31 --- /dev/null +++ b/src/hdmi_design.vhd @@ -0,0 +1,292 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Create Date: 22.07.2015 21:10:34 +-- Module Name: hdmi_design - Behavioral +-- Project Name: +-- +-- Description: Top level of a video processing design +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +entity hdmi_design is + Port ( + clk100 : in STD_LOGIC; + -- Control signals + led : out std_logic_vector(7 downto 0) :=(others => '0'); + sw : in std_logic_vector(2 downto 0) :=(others => '0'); + debug_pmod : out std_logic_vector(7 downto 0) :=(others => '0'); + + --HDMI input signals + hdmi_rx_cec : inout std_logic; + hdmi_rx_hpa : out std_logic; + hdmi_rx_scl : in std_logic; + hdmi_rx_sda : inout std_logic; + hdmi_rx_txen : out std_logic; + hdmi_rx_clk_n : in std_logic; + hdmi_rx_clk_p : in std_logic; + hdmi_rx_n : in std_logic_vector(2 downto 0); + hdmi_rx_p : in std_logic_vector(2 downto 0); + + --- HDMI out + hdmi_tx_cec : inout std_logic; + hdmi_tx_clk_n : out std_logic; + hdmi_tx_clk_p : out std_logic; + hdmi_tx_hpd : in std_logic; + hdmi_tx_rscl : inout std_logic; + hdmi_tx_rsda : inout std_logic; + hdmi_tx_p : out std_logic_vector(2 downto 0); + hdmi_tx_n : out std_logic_vector(2 downto 0) + ); +end hdmi_design; + +architecture Behavioral of hdmi_design is + component hdmi_io is + Port ( + clk100 : in STD_LOGIC; + ------------------------------- + -- Control signals + ------------------------------- + clock_locked : out std_logic; + data_synced : out std_logic; + debug : out std_logic_vector(7 downto 0); + ------------------------------- + --HDMI input signals + ------------------------------- + hdmi_rx_cec : inout std_logic; + hdmi_rx_hpa : out std_logic; + hdmi_rx_scl : in std_logic; + hdmi_rx_sda : inout std_logic; + hdmi_rx_txen : out std_logic; + hdmi_rx_clk_n : in std_logic; + hdmi_rx_clk_p : in std_logic; + hdmi_rx_n : in std_logic_vector(2 downto 0); + hdmi_rx_p : in std_logic_vector(2 downto 0); + + ------------- + -- HDMI out + ------------- + hdmi_tx_cec : inout std_logic; + hdmi_tx_clk_n : out std_logic; + hdmi_tx_clk_p : out std_logic; + hdmi_tx_hpd : in std_logic; + hdmi_tx_rscl : inout std_logic; + hdmi_tx_rsda : inout std_logic; + hdmi_tx_p : out std_logic_vector(2 downto 0); + hdmi_tx_n : out std_logic_vector(2 downto 0); + + pixel_clk : out std_logic; + ------------------------------- + -- VGA data recovered from HDMI + ------------------------------- + in_hdmi_detected : out std_logic; + in_blank : out std_logic; + in_hsync : out std_logic; + in_vsync : out std_logic; + in_red : out std_logic_vector(7 downto 0); + in_green : out std_logic_vector(7 downto 0); + in_blue : out std_logic_vector(7 downto 0); + + ------------------------------------- + -- Audio Levels + ------------------------------------- + audio_channel : out std_logic_vector(2 downto 0); + audio_de : out std_logic; + audio_sample : out std_logic_vector(23 downto 0); + + ----------------------------------- + -- VGA data to be converted to HDMI + ----------------------------------- + out_blank : in std_logic; + out_hsync : in std_logic; + out_vsync : in std_logic; + out_red : in std_logic_vector(7 downto 0); + out_green : in std_logic_vector(7 downto 0); + out_blue : in std_logic_vector(7 downto 0) + ); + end component; + + component pixel_processing is + Port ( clk : in STD_LOGIC; + ------------------ + -- Incoming pixels + ------------------ + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_red : in std_logic_vector(7 downto 0); + in_green : in std_logic_vector(7 downto 0); + in_blue : in std_logic_vector(7 downto 0); + + ------------------- + -- Processed pixels + ------------------- + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_red : out std_logic_vector(7 downto 0); + out_green : out std_logic_vector(7 downto 0); + out_blue : out std_logic_vector(7 downto 0); + + ------------------------------------- + -- Audio samples for metering + ------------------------------------- + audio_channel : in std_logic_vector(2 downto 0); + audio_de : in std_logic; + audio_sample : in std_logic_vector(23 downto 0) + ); + end component; + + signal pixel_clk : std_logic; + signal in_blank : std_logic; + signal in_hsync : std_logic; + signal in_vsync : std_logic; + signal in_red : std_logic_vector(7 downto 0); + signal in_green : std_logic_vector(7 downto 0); + signal in_blue : std_logic_vector(7 downto 0); + signal out_blank : std_logic; + signal out_hsync : std_logic; + signal out_vsync : std_logic; + signal out_red : std_logic_vector(7 downto 0); + signal out_green : std_logic_vector(7 downto 0); + signal out_blue : std_logic_vector(7 downto 0); + + signal audio_channel : std_logic_vector(2 downto 0); + signal audio_de : std_logic; + signal audio_sample : std_logic_vector(23 downto 0); + + signal debug : std_logic_vector(7 downto 0); +begin + debug_pmod <= debug; + led <= debug; + +i_hdmi_io: hdmi_io port map ( + clk100 => clk100, + --------------------- + -- Control signals + --------------------- + clock_locked => open, + data_synced => open, + debug => debug, + --------------------- + -- HDMI input signals + --------------------- + hdmi_rx_cec => hdmi_rx_cec, + hdmi_rx_hpa => hdmi_rx_hpa, + hdmi_rx_scl => hdmi_rx_scl, + hdmi_rx_sda => hdmi_rx_sda, + hdmi_rx_txen => hdmi_rx_txen, + hdmi_rx_clk_n => hdmi_rx_clk_n, + hdmi_rx_clk_p => hdmi_rx_clk_p, + hdmi_rx_p => hdmi_rx_p, + hdmi_rx_n => hdmi_rx_n, + + ---------------------- + -- HDMI output signals + ---------------------- + hdmi_tx_cec => hdmi_tx_cec, + hdmi_tx_clk_n => hdmi_tx_clk_n, + hdmi_tx_clk_p => hdmi_tx_clk_p, + hdmi_tx_hpd => hdmi_tx_hpd, + hdmi_tx_rscl => hdmi_tx_rscl, + hdmi_tx_rsda => hdmi_tx_rsda, + hdmi_tx_p => hdmi_tx_p, + hdmi_tx_n => hdmi_tx_n, + + + pixel_clk => pixel_clk, + ------------------------------- + -- VGA data recovered from HDMI + ------------------------------- + in_blank => in_blank, + in_hsync => in_hsync, + in_vsync => in_vsync, + in_red => in_red, + in_green => in_green, + in_blue => in_blue, + + audio_channel => audio_channel, + audio_de => audio_de, + audio_sample => audio_sample, + + ----------------------------------- + -- VGA data to be converted to HDMI + ----------------------------------- + out_blank => out_blank, + out_hsync => out_hsync, + out_vsync => out_vsync, + out_red => out_red, + out_green => out_green, + out_blue => out_blue + ); + +i_processing: pixel_processing Port map ( + clk => pixel_clk, + ------------------ + -- Incoming pixels + ------------------ + in_blank => in_blank, + in_hsync => in_hsync, + in_vsync => in_vsync, + in_red => in_red, + in_green => in_green, + in_blue => in_blue, + + audio_channel => audio_channel, + audio_de => audio_de, + audio_sample => audio_sample, + ------------------- + -- Processed pixels + ------------------- + out_blank => out_blank, + out_hsync => out_hsync, + out_vsync => out_vsync, + out_red => out_red, + out_green => out_green, + out_blue => out_blue + ); + +end Behavioral; \ No newline at end of file diff --git a/src/hdmi_input.vhd b/src/hdmi_input.vhd new file mode 100644 index 0000000..8a66d7a --- /dev/null +++ b/src/hdmi_input.vhd @@ -0,0 +1,683 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: hdmi_input - Behavioral +-- +-- Description: Decode the video data out of an incoming HDMI data stream. +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +library UNISIM; +use UNISIM.VComponents.all; + +entity hdmi_input is + Port ( + system_clk : in std_logic; + + debug : out std_logic_vector(5 downto 0); + hdmi_detected : out std_logic; + + pixel_clk : out std_logic; -- Driven by BUFG + pixel_io_clk_x1 : out std_logic; -- Driven by BUFFIO + pixel_io_clk_x5 : out std_logic; -- Driven by BUFFIO + + -- HDMI input signals + hdmi_in_clk : in std_logic; + hdmi_in_ch0 : in std_logic; + hdmi_in_ch1 : in std_logic; + hdmi_in_ch2 : in std_logic; + + -- Status + pll_locked : out std_logic; + symbol_sync : out std_logic; + + -- Raw data signals + raw_blank : out std_logic; + raw_hsync : out std_logic; + raw_vsync : out std_logic; + raw_ch0 : out std_logic_vector(7 downto 0); + raw_ch1 : out std_logic_vector(7 downto 0); + raw_ch2 : out std_logic_vector(7 downto 0); + -- ADP data + adp_data_valid : out std_logic; + adp_header_bit : out std_logic; + adp_frame_bit : out std_logic; + adp_subpacket0_bits : out std_logic_vector(1 downto 0); + adp_subpacket1_bits : out std_logic_vector(1 downto 0); + adp_subpacket2_bits : out std_logic_vector(1 downto 0); + adp_subpacket3_bits : out std_logic_vector(1 downto 0) + ); +end hdmi_input; + +architecture Behavioral of hdmi_input is + + component input_channel is + Port ( clk_mgmt : in STD_LOGIC; + clk : in STD_LOGIC; + clk_x1 : in STD_LOGIC; + clk_x5 : in STD_LOGIC; + serial : in STD_LOGIC; + reset : in STD_LOGIC; + ce : in STD_LOGIC; + invalid_symbol : out std_logic; + ctl_valid : out std_logic; + ctl : out std_logic_vector (1 downto 0); + terc4_valid : out std_logic; + terc4 : out std_logic_vector (3 downto 0); + guardband_valid : out std_logic; + guardband : out std_logic_vector (0 downto 0); + data_valid : out std_logic; + data : out std_logic_vector (7 downto 0); + symbol_sync : out STD_LOGIC); + end component; + + signal clk_pixel_raw : std_logic; + component deserialiser_1_to_10 is + Port ( clk_mgmt : in std_logic; + delay_ce : in std_logic; + delay_count : in std_logic_vector (4 downto 0); + clk : in std_logic; + clk_x1 : in std_logic; + bitslip : in std_logic; + clk_x5 : in std_logic; + reset : in std_logic; + serial : in std_logic; + data : out std_logic_vector (9 downto 0)); + end component; + + component TMDS_decoder is + Port ( clk : in std_logic; + symbol : in std_logic_vector (9 downto 0); + invalid_symbol : out std_logic; + ctl_valid : out std_logic; + ctl : out std_logic_vector (1 downto 0); + terc4_valid : out std_logic; + terc4 : out std_logic_vector (3 downto 0); + guardband_valid : out std_logic; + guardband : out std_logic_vector (0 downto 0); + data_valid : out std_logic; + data : out std_logic_vector (7 downto 0)); + end component; + + component alingment_detect is + Port ( clk : in STD_LOGIC; + invalid_symbol : in STD_LOGIC; + delay_count : out STD_LOGIC_VECTOR(4 downto 0); + delay_ce : out STD_LOGIC; + bitslip : out STD_LOGIC; + symbol_sync : out STD_LOGIC); + end component; + + signal clk_pixel : std_logic; + signal clk_pixel_x1 : std_logic; + signal clk_pixel_x5 : std_logic; + signal clk_pixel_x1_raw : std_logic; + signal clk_pixel_x5_raw : std_logic; + signal clk_200_raw : std_logic; + signal clk_200 : std_logic; + signal clkfb_1 : std_logic; + signal clkfb_2 : std_logic; + signal locked : std_logic; + signal reset : std_logic; + signal ser_reset : std_logic; + signal ser_ce : std_logic; + ------------------------------------------------------------- + -- The raw 10-bit received symbols + ------------------------------------------------------------- + signal ch0_symbol : std_logic_vector(9 downto 0); + signal ch1_symbol : std_logic_vector(9 downto 0); + signal ch2_symbol : std_logic_vector(9 downto 0); + + ------------------------------------------------------------- + -- For the decoded TMDS data + ------------------------------------------------------------- + signal ch0_invalid_symbol : std_logic; + signal ch0_ctl_valid : std_logic; + signal ch0_ctl : std_logic_vector(1 downto 0); + signal ch0_terc4_valid : std_logic; + signal ch0_terc4 : std_logic_vector (3 downto 0); + signal ch0_data_valid : std_logic; + signal ch0_data : std_logic_vector(7 downto 0); + signal ch0_guardband_valid : std_logic; + signal ch0_guardband : std_logic_vector (0 downto 0); + signal ch0_delay_count : std_logic_vector (4 downto 0); + signal ch0_delay_ce : STD_LOGIC; + signal ch0_bitslip : STD_LOGIC; + signal ch0_symbol_sync : STD_LOGIC; + + signal ch0_invalid_symbol_1 : std_logic; + signal ch0_ctl_valid_1 : std_logic; + signal ch0_ctl_1 : std_logic_vector(1 downto 0); + signal ch0_terc4_valid_1 : std_logic; + signal ch0_terc4_1 : std_logic_vector (3 downto 0); + signal ch0_data_valid_1 : std_logic; + signal ch0_data_1 : std_logic_vector(7 downto 0); + + signal ch1_invalid_symbol : std_logic; + signal ch1_ctl_valid : std_logic; + signal ch1_ctl : std_logic_vector(1 downto 0); + signal ch1_terc4_valid : std_logic; + signal ch1_terc4 : std_logic_vector (3 downto 0); + signal ch1_data_valid : std_logic; + signal ch1_data : std_logic_vector(7 downto 0); + signal ch1_guardband_valid : std_logic; + signal ch1_guardband : std_logic_vector (0 downto 0); + signal ch1_delay_count : std_logic_vector (4 downto 0); + signal ch1_delay_ce : STD_LOGIC; + signal ch1_bitslip : STD_LOGIC; + signal ch1_symbol_sync : STD_LOGIC; + + signal ch1_invalid_symbol_1 : std_logic; + signal ch1_ctl_valid_1 : std_logic; + signal ch1_ctl_1 : std_logic_vector(1 downto 0); + signal ch1_terc4_valid_1 : std_logic; + signal ch1_terc4_1 : std_logic_vector (3 downto 0); + signal ch1_data_valid_1 : std_logic; + signal ch1_data_1 : std_logic_vector(7 downto 0); + + signal ch2_invalid_symbol : std_logic; + signal ch2_ctl_valid : std_logic; + signal ch2_ctl : std_logic_vector(1 downto 0); + signal ch2_terc4_valid : std_logic; + signal ch2_terc4 : std_logic_vector (3 downto 0); + signal ch2_data_valid : std_logic; + signal ch2_data : std_logic_vector(7 downto 0); + signal ch2_guardband_valid : std_logic; + signal ch2_guardband : std_logic_vector (0 downto 0); + signal ch2_delay_count : std_logic_vector (4 downto 0); + signal ch2_delay_ce : STD_LOGIC; + signal ch2_bitslip : STD_LOGIC; + signal ch2_symbol_sync : STD_LOGIC; + + signal ch2_invalid_symbol_1 : std_logic; + signal ch2_ctl_valid_1 : std_logic; + signal ch2_ctl_1 : std_logic_vector(1 downto 0); + signal ch2_terc4_valid_1 : std_logic; + signal ch2_terc4_1 : std_logic_vector (3 downto 0); + signal ch2_data_valid_1 : std_logic; + signal ch2_data_1 : std_logic_vector(7 downto 0); + + + signal reset_counter : unsigned(7 downto 0) := (others => '1'); + + signal vdp_prefix_detect : std_logic_vector(7 downto 0) := (others => '0'); + signal vdp_guardband_detect : std_logic := '0'; + signal vdp_prefix_seen : std_logic := '0'; + signal in_vdp : std_logic := '0'; + + signal adp_prefix_detect : std_logic_vector(7 downto 0) := (others => '0'); + signal adp_guardband_detect : std_logic := '0'; + signal adp_prefix_seen : std_logic := '0'; + signal in_adp : std_logic := '0'; + signal dvid_mode : std_logic := '0'; + signal last_was_ctl : std_logic := '0'; + + signal in_dvid : std_logic := '0'; + signal symbol_sync_i : std_logic := '0'; +begin + pll_locked <= locked; + symbol_sync <= symbol_sync_i; + reset <= std_logic(reset_counter(reset_counter'high)); + + debug <= ch2_invalid_symbol & ch1_invalid_symbol & ch0_invalid_symbol & dvid_mode & locked & symbol_sync_i; + + -------------------------------------------- + -- a 200MHz clock for the IDELAY reference + -------------------------------------------- +clk_MMCME2_BASE_inst : MMCME2_BASE + generic map ( + BANDWIDTH => "OPTIMIZED", -- Jitter programming (OPTIMIZED, HIGH, LOW) + DIVCLK_DIVIDE => 1, -- Master division value (1-106) + CLKFBOUT_MULT_F => 8.0, -- Multiply value for all CLKOUT (2.000-64.000). + 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 - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128) + CLKOUT0_DIVIDE_F => 4.0, -- Divide amount for CLKOUT0 (1.000-128.000). + CLKOUT1_DIVIDE => 1, + CLKOUT2_DIVIDE => 1, + CLKOUT3_DIVIDE => 1, + CLKOUT4_DIVIDE => 1, + CLKOUT5_DIVIDE => 1, + CLKOUT6_DIVIDE => 1, + -- CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99). + 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, + CLKOUT6_DUTY_CYCLE => 0.5, + -- CLKOUT0_PHASE - CLKOUT6_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, + CLKOUT6_PHASE => 0.0, + CLKOUT4_CASCADE => FALSE, -- Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE) + REF_JITTER1 => 0.0, -- Reference input jitter in UI (0.000-0.999). + STARTUP_WAIT => FALSE -- Delays DONE until MMCM is locked (FALSE, TRUE) + ) + port map ( + -- Clock Outputs: 1-bit (each) output: User configurable clock outputs + CLKOUT0 => clk_200_raw, -- 1-bit output: CLKOUT0 + CLKOUT0B => open, -- 1-bit output: Inverted CLKOUT0 + CLKOUT1 => open, -- 1-bit output: CLKOUT1 + CLKOUT1B => open, -- 1-bit output: Inverted CLKOUT1 + CLKOUT2 => open, -- 1-bit output: CLKOUT2 + CLKOUT2B => open, -- 1-bit output: Inverted CLKOUT2 + CLKOUT3 => open, -- 1-bit output: CLKOUT3 + CLKOUT3B => open, -- 1-bit output: Inverted CLKOUT3 + CLKOUT4 => open, -- 1-bit output: CLKOUT4 + CLKOUT5 => open, -- 1-bit output: CLKOUT5 + CLKOUT6 => open, -- 1-bit output: CLKOUT6 + -- Feedback Clocks: 1-bit (each) output: Clock feedback ports + CLKFBOUT => clkfb_1, -- 1-bit output: Feedback clock + CLKFBOUTB => open, -- 1-bit output: Inverted CLKFBOUT + -- Status Ports: 1-bit (each) output: MMCM status ports + LOCKED => open, -- 1-bit output: LOCK + -- Clock Inputs: 1-bit (each) input: Clock input + CLKIN1 => system_clk, -- 1-bit input: Clock + -- Control Ports: 1-bit (each) input: MMCM control ports + PWRDWN => '0', -- 1-bit input: Power-down + RST => '0', -- 1-bit input: Reset + -- Feedback Clocks: 1-bit (each) input: Clock feedback ports + CLKFBIN => clkfb_1 -- 1-bit input: Feedback clock + ); + +i_BUFG: BUFG PORT MAP ( + I => clk_200_raw, + O => clk_200 + ); + ------------------------------ + -- Input Delay reference + -- + -- These are tied to the delay instances + -- by the IODELAY_GROUP attribute. + -------------------------------------------- +IDELAYCTRL_inst : IDELAYCTRL + port map ( + RDY => open, -- 1-bit output: Ready output + REFCLK => clk_200, -- 1-bit input: Reference clock input + RST => '0' -- 1-bit input: Active high reset input + ); + + -------------------------------- + -- MMCM driven by the HDMI clock + -------------------------------- +hdmi_MMCME2_BASE_inst : MMCME2_BASE + generic map ( + BANDWIDTH => "OPTIMIZED", -- Jitter programming (OPTIMIZED, HIGH, LOW) + DIVCLK_DIVIDE => 1, -- Master division value (1-106) + CLKFBOUT_MULT_F => 5.0, -- Multiply value for all CLKOUT (2.000-64.000). + CLKFBOUT_PHASE => 0.0, -- Phase offset in degrees of CLKFB (-360.000-360.000). + CLKIN1_PERIOD => 12.5, --1000.0/148.5, -- Input clock period in ns to ps resolution (i.e. 33.333 is 30 MHz). + -- CLKOUT0_DIVIDE - CLKOUT6_DIVIDE: Divide amount for each CLKOUT (1-128) + CLKOUT0_DIVIDE_F => 5.0, -- Divide amount for CLKOUT0 (1.000-128.000). + CLKOUT1_DIVIDE => 5, + CLKOUT2_DIVIDE => 1, + CLKOUT3_DIVIDE => 1, + CLKOUT4_DIVIDE => 1, + CLKOUT5_DIVIDE => 1, + CLKOUT6_DIVIDE => 1, + -- CLKOUT0_DUTY_CYCLE - CLKOUT6_DUTY_CYCLE: Duty cycle for each CLKOUT (0.01-0.99). + 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, + CLKOUT6_DUTY_CYCLE => 0.5, + -- CLKOUT0_PHASE - CLKOUT6_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, + CLKOUT6_PHASE => 0.0, + CLKOUT4_CASCADE => FALSE, -- Cascade CLKOUT4 counter with CLKOUT6 (FALSE, TRUE) + REF_JITTER1 => 0.0, -- Reference input jitter in UI (0.000-0.999). + STARTUP_WAIT => FALSE -- Delays DONE until MMCM is locked (FALSE, TRUE) + ) + port map ( + -- Clock Outputs: 1-bit (each) output: User configurable clock outputs + CLKOUT0 => clk_pixel_raw, -- 1-bit output: CLKOUT0 + CLKOUT0B => open, -- 1-bit output: Inverted CLKOUT0 + CLKOUT1 => clk_pixel_x1_raw, -- 1-bit output: CLKOUT1 + CLKOUT1B => open, -- 1-bit output: Inverted CLKOUT1 + CLKOUT2 => clk_pixel_x5_raw, -- 1-bit output: CLKOUT2 + CLKOUT2B => open, -- 1-bit output: Inverted CLKOUT2 + CLKOUT3 => open, -- 1-bit output: CLKOUT3 + CLKOUT3B => open, -- 1-bit output: Inverted CLKOUT3 + CLKOUT4 => open, -- 1-bit output: CLKOUT4 + CLKOUT5 => open, -- 1-bit output: CLKOUT5 + CLKOUT6 => open, -- 1-bit output: CLKOUT6 + -- Feedback Clocks: 1-bit (each) output: Clock feedback ports + CLKFBOUT => clkfb_2, -- 1-bit output: Feedback clock + CLKFBOUTB => open, -- 1-bit output: Inverted CLKFBOUT + -- Status Ports: 1-bit (each) output: MMCM status ports + LOCKED => locked, -- 1-bit output: LOCK + -- Clock Inputs: 1-bit (each) input: Clock input + CLKIN1 => hdmi_in_clk, -- 1-bit input: Clock + -- Control Ports: 1-bit (each) input: MMCM control ports + PWRDWN => '0', -- 1-bit input: Power-down + RST => '0', -- 1-bit input: Reset + -- Feedback Clocks: 1-bit (each) input: Clock feedback ports + CLKFBIN => clkfb_2 -- 1-bit input: Feedback clock + ); + + ---------------------------------- + -- Force the highest speed clock + -- through the IO clock buffer + -- (this is only rated for 600MHz!) + ----------------------------------- +BUFIO_x5_inst : BUFIO + port map ( + I => clk_pixel_x5_raw, -- 1-bit input: Clock input (connect to an IBUF or BUFMR). + O => clk_pixel_x5 -- 1-bit output: Clock output (connect to I/O clock loads). + ); + +BUFIO_x1_inst : BUFG + port map ( + I => clk_pixel_x1_raw, -- 1-bit input: Clock input (connect to an IBUF or BUFMR). + O => clk_pixel_x1 -- 1-bit output: Clock output (connect to I/O clock loads). + ); + +BUFIO_inst : BUFG + port map ( + I => clk_pixel_raw, -- 1-bit input: Clock input (connect to an IBUF or BUFMR). + O => clk_pixel -- 1-bit output: Clock output (connect to I/O clock loads). + ); + pixel_clk <= clk_pixel; + pixel_io_clk_x1 <= clk_pixel_x1; + pixel_io_clk_x5 <= clk_pixel_x5; + +ch0: input_channel Port map ( + clk_mgmt => system_clk, + clk => clk_pixel, + ce => ser_ce, + clk_x1 => clk_pixel_x1, + clk_x5 => clk_pixel_x5, + serial => hdmi_in_ch0, + invalid_symbol => ch0_invalid_symbol, + ctl_valid => ch0_ctl_valid, + ctl => ch0_ctl, + terc4_valid => ch0_terc4_valid, + terc4 => ch0_terc4, + guardband_valid => ch0_guardband_valid, + guardband => ch0_guardband, + data_valid => ch0_data_valid, + data => ch0_data, + reset => ser_reset, + symbol_sync => ch0_symbol_sync); + +ch1: input_channel Port map ( + clk_mgmt => system_clk, + clk => clk_pixel, + ce => ser_ce, + clk_x1 => clk_pixel_x1, + clk_x5 => clk_pixel_x5, + serial => hdmi_in_ch1, + invalid_symbol => ch1_invalid_symbol, + ctl_valid => ch1_ctl_valid, + ctl => ch1_ctl, + terc4_valid => ch1_terc4_valid, + terc4 => ch1_terc4, + guardband_valid => ch1_guardband_valid, + guardband => ch1_guardband, + data_valid => ch1_data_valid, + data => ch1_data, + reset => ser_reset, + symbol_sync => ch1_symbol_sync); + +ch2: input_channel Port map ( + clk_mgmt => system_clk, + clk => clk_pixel, + ce => ser_ce, + clk_x1 => clk_pixel_x1, + clk_x5 => clk_pixel_x5, + serial => hdmi_in_ch2, + invalid_symbol => ch2_invalid_symbol, + ctl_valid => ch2_ctl_valid, + ctl => ch2_ctl, + terc4_valid => ch2_terc4_valid, + terc4 => ch2_terc4, + guardband_valid => ch2_guardband_valid, + guardband => ch2_guardband, + data_valid => ch2_data_valid, + data => ch2_data, + reset => ser_reset, + symbol_sync => ch2_symbol_sync); + + symbol_sync_i <= ch0_symbol_sync and ch1_symbol_sync and ch2_symbol_sync; + + hdmi_detected <= not dvid_mode; +hdmi_section_decode: process(clk_pixel) + begin + if rising_edge(clk_pixel) then + ------------------------------------------------------------------- + -- Output the values depending on what sort of data block we are in + ------------------------------------------------------------------- + if ch0_ctl_valid = '1' and ch1_ctl_valid = '1' and ch2_ctl_valid = '1' then + ------------------------------------------------------------------- + -- As soon as we see avalid CTL symbols we are no longer in the + -- video or aux data period it doesn't have any trailing guard band + ------------------------------------------------------------------- + in_vdp <= '0'; + in_adp <= '0'; + in_dvid <= '0'; + raw_vsync <= ch0_ctl(1); + raw_hsync <= ch0_ctl(0); + raw_blank <= '1'; + raw_ch2 <= (others => '0'); + raw_ch1 <= (others => '0'); + raw_ch0 <= (others => '0'); + last_was_ctl <= '1'; + adp_data_valid <= '0'; + else + last_was_ctl <= '0'; + adp_data_valid <= '0'; + if in_vdp = '1' then + raw_vsync <= '0'; + raw_hsync <= '0'; + raw_blank <= '0'; + raw_ch2 <= ch2_data; + raw_ch1 <= ch1_data; + raw_ch0 <= ch0_data; + if ch2_invalid_symbol = '1' or ch2_invalid_symbol = '1' or ch2_invalid_symbol = '1' then + raw_ch2 <= x"EF"; + raw_ch1 <= x"16"; + raw_ch0 <= x"16"; + end if; + + elsif in_dvid = '1' then + -- In the Video data period + raw_vsync <= '0'; + raw_hsync <= '0'; + raw_blank <= '0'; + raw_ch2 <= ch2_data; + raw_ch1 <= ch1_data; + raw_ch0 <= ch0_data; + elsif in_adp = '1' then + -- In the Aux Data Period Period + raw_vsync <= ch0_terc4(1); + raw_hsync <= ch0_terc4(0); + raw_blank <= '1'; + raw_ch0 <= (others => '0'); + raw_ch1 <= (others => '0'); + raw_ch2 <= (others => '0'); + -- ADP data extraction + adp_data_valid <= '1'; + adp_header_bit <= ch0_terc4(2); + adp_frame_bit <= ch0_terc4(3); + adp_subpacket0_bits <= ch2_terc4(0) & ch1_terc4(0); + adp_subpacket1_bits <= ch2_terc4(1) & ch1_terc4(1); + adp_subpacket2_bits <= ch2_terc4(2) & ch1_terc4(2); + adp_subpacket3_bits <= ch2_terc4(3) & ch1_terc4(3); + end if; + end if; + + ------------------------------------------------------------ + -- We need to detect 8 ADP or VDP prefix characters in a row + ------------------------------------------------------------ + vdp_prefix_detect <= vdp_prefix_detect(6 downto 0) & '0'; + vdp_prefix_seen <= '0'; + if ch0_ctl_valid = '1' and ch1_ctl_valid = '1' and ch1_ctl_valid = '1' then + if ch1_ctl = "01" and ch2_ctl = "00" then + vdp_prefix_detect(0) <= '1'; + if vdp_prefix_detect = "01111111" then + vdp_prefix_seen <= '1'; + end if; + end if; + end if; + + --------------------------------------------- + -- See if we can detect the ADP guardband + -- + -- The ADP guardband includes HSYNC and VSYNC + -- encoded in TERC4 coded in Ch0. + --------------------------------------------- + adp_prefix_detect <= adp_prefix_detect(6 downto 0) & '0'; + adp_prefix_seen <= '0'; + if ch0_ctl_valid = '1' and ch1_ctl_valid = '1' and ch1_ctl_valid = '1' then + if ch1_ctl = "01" and ch2_ctl = "01" then + adp_prefix_detect(0) <= '1'; + if adp_prefix_detect = "01111111" then + adp_prefix_seen <= '1'; + end if; + end if; + end if; + --------------------------------------------- + -- See if we can detect the ADP guardband + -- + -- The ADP guardband includes HSYNC and VSYNC + -- encoded in TERC4 coded in Ch0 - annoying! + --------------------------------------------- + adp_guardband_detect <= '0'; + if ch0_terc4_valid = '1' and ch1_guardband_valid = '1' and ch1_guardband_valid = '1' then + if ch0_terc4(3 downto 2) = "11" and ch1_guardband = "0" and ch2_guardband = "0" then + raw_vsync <= ch0_terc4(1); + raw_hsync <= ch0_terc4(0); + adp_guardband_detect <= adp_prefix_seen; + in_adp <= adp_guardband_detect AND (not in_adp) and (not in_vdp); + end if; + end if; + ----------------------------------------- + -- See if we can detect the VDP guardband + -- This is pretty nices as the guard + ----------------------------------------- + vdp_guardband_detect <= '0'; + if ch0_guardband_valid = '1' and ch1_guardband_valid = '1' and ch2_guardband_valid = '1' then + -- TERC Coded for the VDP guard band. + if ch0_guardband = "1" and ch1_guardband = "0" and ch2_guardband = "1" then + vdp_guardband_detect <= vdp_prefix_seen; + in_vdp <= vdp_guardband_detect AND (not in_adp) and (not in_vdp); + dvid_mode <= '0'; + end if; + end if; + -------------------------------- + -- Is this some DVID video data? + -------------------------------- + if dvid_mode = '1' and last_was_ctl = '1' and ch0_data_valid = '1' and ch1_data_valid = '1' and ch2_data_valid = '1' then + in_dvid <= '1'; + end if; + ------------------------------------------------------------- + -- Is this an un-announced video data? If so we receiving + -- DVI-D data, and not HDMI + ------------------------------------------------------------- + if ch0_data_valid = '1' and ch1_data_valid = '1' and ch2_data_valid = '1' + and last_was_ctl = '1' and vdp_prefix_seen = '0' and adp_prefix_seen = '0' then + dvid_mode <= '1'; + end if; + + ch0_invalid_symbol_1 <= ch0_invalid_symbol; + ch0_ctl_valid_1 <= ch0_ctl_valid; + ch0_ctl_1 <= ch0_ctl; + ch0_terc4_valid_1 <= ch0_terc4_valid; + ch0_terc4_1 <= ch0_terc4; + ch0_data_1 <= ch0_data; + + ch1_invalid_symbol_1 <= ch1_invalid_symbol; + ch1_ctl_valid_1 <= ch1_ctl_valid; + ch1_ctl_1 <= ch1_ctl; + ch1_terc4_valid_1 <= ch1_terc4_valid; + ch1_terc4_1 <= ch1_terc4; + ch1_data_1 <= ch1_data; + + ch2_invalid_symbol_1 <= ch2_invalid_symbol; + ch2_ctl_valid_1 <= ch2_ctl_valid; + ch2_ctl_1 <= ch2_ctl; + ch2_terc4_valid_1 <= ch2_terc4_valid; + ch2_terc4_1 <= ch2_terc4; + ch2_data_valid_1 <= ch2_data_valid; + ch2_data_1 <= ch2_data; + end if; + end process; + +------------------------------------------ +-- Reset the receivers if PLL lock is lost +------------------------------------------ +reset_proc: process(system_clk) + begin + if rising_edge(system_clk) then + if locked = '1' then + if reset_counter > 0 then + reset_counter <= reset_counter-1; + end if; + else + reset_counter <= (others => '1'); + end if; + end if; + end process; + +reset_proc2: process(clk_pixel) + begin + if rising_edge(clk_pixel) then + ser_reset <= reset_counter(reset_counter'high); + ser_ce <= not ser_reset; + end if; + end process; +end Behavioral; diff --git a/src/hdmi_io.vhd b/src/hdmi_io.vhd new file mode 100644 index 0000000..27b2675 --- /dev/null +++ b/src/hdmi_io.vhd @@ -0,0 +1,531 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field '0'); + +i_edid_rom: edid_rom port map ( + clk => clk100, + sclk_raw => hdmi_rx_scl, + sdat_raw => hdmi_rx_sda, + edid_debug => debug(2 downto 0)); + + --------------------- + -- Input buffers + --------------------- +in_clk_buf: IBUFDS generic map ( IOSTANDARD => "TMDS_33") + port map ( I => hdmi_rx_clk_p, IB => hdmi_rx_clk_n, O => tmds_in_clk); + +in_rx0_buf: IBUFDS generic map ( IOSTANDARD => "TMDS_33") + port map ( I => hdmi_rx_p(0), IB => hdmi_rx_n(0), O => tmds_in_ch0); + +in_rx1_buf: IBUFDS generic map ( IOSTANDARD => "TMDS_33") + port map ( I => hdmi_rx_p(1), IB => hdmi_rx_n(1), O => tmds_in_ch1); + +in_rx2_buf: IBUFDS generic map ( IOSTANDARD => "TMDS_33") + port map ( I => hdmi_rx_p(2), IB => hdmi_rx_n(2), O => tmds_in_ch2); + +i_hdmi_input : hdmi_input port map ( + system_clk => clk100, + debug => open, + -- Pixel and serializer clocks + pixel_clk => pixel_clk_i, + pixel_io_clk_x1 => pixel_io_clk_x1, + pixel_io_clk_x5 => pixel_io_clk_x5, + --- HDMI input signals + hdmi_in_clk => tmds_in_clk, + hdmi_in_ch0 => tmds_in_ch0, + hdmi_in_ch1 => tmds_in_ch1, + hdmi_in_ch2 => tmds_in_ch2, + -- are the HDMI symbols in sync? + symbol_sync => data_synced, + pll_locked => clock_locked, + -- VGA internal Signals + hdmi_detected => in_hdmi_detected, + raw_blank => raw_blank, + raw_hsync => raw_hsync, + raw_vsync => raw_vsync, + raw_ch2 => raw_ch2, + raw_ch1 => raw_ch1, + raw_ch0 => raw_ch0, + -- ADP data + adp_data_valid => adp_data_valid, + adp_header_bit => adp_header_bit, + adp_frame_bit => adp_frame_bit, + adp_subpacket0_bits => adp_subpacket0_bits, + adp_subpacket1_bits => adp_subpacket1_bits, + adp_subpacket2_bits => adp_subpacket2_bits, + adp_subpacket3_bits => adp_subpacket3_bits + ); + + ------------------------------------- + -- If the input data is in 422 format + -- then convert it to 12-bit 444 data + ------------------------------------- +i_expand_422_to_444: expand_422_to_444 Port map ( + clk => pixel_clk_i, + input_is_422 => input_is_422, + ------------------ + -- Incoming raw data + ------------------ + in_blank => raw_blank, + in_hsync => raw_hsync, + in_vsync => raw_vsync, + in_ch2 => raw_ch2, + in_ch1 => raw_ch1, + in_ch0 => raw_ch0, + + ------------------- + -- Processed pixels + ------------------- + out_blank => fourfourfour_blank, + out_hsync => fourfourfour_hsync, + out_vsync => fourfourfour_vsync, + out_U => fourfourfour_U, + out_V => fourfourfour_V, + out_W => fourfourfour_W + ); + +i_conversion_to_RGB: conversion_to_RGB + port map ( + clk => pixel_clk_i, + ------------------------ + input_is_YCbCr => input_is_YCbCr, + input_is_sRGB => input_is_sRGB, + in_blank => fourfourfour_blank, + in_hsync => fourfourfour_hsync, + in_vsync => fourfourfour_vsync, + in_U => fourfourfour_U, + in_V => fourfourfour_V, + in_W => fourfourfour_W, + ------------------------ + out_blank => rgb_blank, + out_hsync => rgb_hsync, + out_vsync => rgb_vsync, + out_R => rgb_R, + out_G => rgb_G, + out_B => rgb_B + ); + + ----------------------------------------- + -- Colour space conversion yet to be done + ----------------------------------------- + in_blank <= rgb_blank; + in_hsync <= rgb_hsync; + in_vsync <= rgb_vsync; + in_blue <= rgb_B(11 downto 4); + in_green <= rgb_G(11 downto 4); + in_red <= rgb_R(11 downto 4); + + ------------------------------------------------ + -- Processing the non-video data #1 + -- Extracting the Video Infopacket data we need + -- to correctly convert the video data + ------------------------------------------------ +i_extract_video_infopacket_data: extract_video_infopacket_data port map ( + clk => pixel_clk_i, + -- ADP data + adp_data_valid => adp_data_valid, + adp_header_bit => adp_header_bit, + adp_frame_bit => adp_frame_bit, + adp_subpacket0_bits => adp_subpacket0_bits, + adp_subpacket1_bits => adp_subpacket1_bits, + adp_subpacket2_bits => adp_subpacket2_bits, + adp_subpacket3_bits => adp_subpacket3_bits, + -- The stuff we need + input_is_YCbCr => input_is_YCbCr, + input_is_422 => input_is_422, + input_is_sRGB => input_is_sRGB +); + ------------------------------------------------ + -- Processing the non-video data #2 + -- Extracting the Audio samples so we can display + -- level menters on the screen + ------------------------------------------------ +i_extract_audio_samples: extract_audio_samples PORT MAP ( + clk => pixel_clk_i, + -- ADP data + adp_data_valid => adp_data_valid, + adp_header_bit => adp_header_bit, + adp_frame_bit => adp_frame_bit, + adp_subpacket0_bits => adp_subpacket0_bits, + adp_subpacket1_bits => adp_subpacket1_bits, + adp_subpacket2_bits => adp_subpacket2_bits, + adp_subpacket3_bits => adp_subpacket3_bits, + -- The stuff we need + audio_de => audio_de, + audio_channel => audio_channel, + audio_sample => audio_sample); + +------------------------------------------------ +-- Outputting video data +----------------------------------------------- +i_DVID_output: DVID_output port map ( + pixel_clk => pixel_clk_i, + pixel_io_clk_x1 => pixel_io_clk_x1, + pixel_io_clk_x5 => pixel_io_clk_x5, + + data_valid => '1', + -- VGA Signals + vga_blank => out_blank, + vga_hsync => out_hsync, + vga_vsync => out_vsync, + vga_red => out_red, + vga_blue => out_blue, + vga_green => out_green, + + --- HDMI out + tmds_out_clk => tmds_out_clk, + tmds_out_ch0 => tmds_out_ch0, + tmds_out_ch1 => tmds_out_ch1, + tmds_out_ch2 => tmds_out_ch2 + ); + + ----------------------------- + -- Other HDMI control signals + ----------------------------- + hdmi_tx_rsda <= 'Z'; + hdmi_tx_cec <= 'Z'; + hdmi_tx_rscl <= '1'; + + ----------------- + -- Output buffers + ----------------- +out_clk_buf: OBUFDS generic map ( IOSTANDARD => "TMDS_33", SLEW => "FAST") + port map ( O => hdmi_tx_clk_p, OB => hdmi_tx_clk_n, I => tmds_out_clk); + +out_tx0_buf: OBUFDS generic map ( IOSTANDARD => "TMDS_33", SLEW => "FAST") + port map ( O => hdmi_tx_p(0), OB => hdmi_tx_n(0), I => tmds_out_ch0); + +out_tx1_buf: OBUFDS generic map ( IOSTANDARD => "TMDS_33", SLEW => "FAST") + port map ( O => hdmi_tx_p(1), OB => hdmi_tx_n(1), I => tmds_out_ch1); + +out_tx2_buf: OBUFDS generic map ( IOSTANDARD => "TMDS_33", SLEW => "FAST") + port map ( O => hdmi_tx_p(2), OB => hdmi_tx_n(2), I => tmds_out_ch2); + +end Behavioral; diff --git a/src/input_channel.vhd b/src/input_channel.vhd new file mode 100644 index 0000000..f68d9ae --- /dev/null +++ b/src/input_channel.vhd @@ -0,0 +1,155 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Create Date: 30.07.2015 23:11:34 +-- Module Name: input_channel - Behavioral +-- +-- Description: Receiving one of the three HDMI input channels. and decoding +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity input_channel is + Port ( clk_mgmt : in STD_LOGIC; + clk : in STD_LOGIC; + clk_x1 : in STD_LOGIC; + clk_x5 : in STD_LOGIC; + serial : in STD_LOGIC; + reset : in std_logic; + ce : in STD_LOGIC; + invalid_symbol : out std_logic; + ctl_valid : out std_logic; + ctl : out std_logic_vector (1 downto 0); + terc4_valid : out std_logic; + terc4 : out std_logic_vector (3 downto 0); + guardband_valid : out std_logic; + guardband : out std_logic_vector (0 downto 0); + data_valid : out std_logic; + data : out std_logic_vector (7 downto 0); + symbol_sync : out STD_LOGIC); +end input_channel; + +architecture Behavioral of input_channel is + component deserialiser_1_to_10 is + Port ( clk_mgmt : in std_logic; + delay_ce : in std_logic; + delay_count : in std_logic_vector (4 downto 0); + ce : in STD_LOGIC; + clk : in std_logic; + clk_x1 : in std_logic; + bitslip : in std_logic; + clk_x5 : in std_logic; + reset : in std_logic; + serial : in std_logic; + data : out std_logic_vector (9 downto 0)); + end component; + + component TMDS_decoder is + Port ( clk : in std_logic; + symbol : in std_logic_vector (9 downto 0); + invalid_symbol : out std_logic; + ctl_valid : out std_logic; + ctl : out std_logic_vector (1 downto 0); + terc4_valid : out std_logic; + terc4 : out std_logic_vector (3 downto 0); + guardband_valid : out std_logic; + guardband : out std_logic_vector (0 downto 0); + data_valid : out std_logic; + data : out std_logic_vector (7 downto 0)); + end component; + + component alingment_detect is + Port ( clk : in STD_LOGIC; + invalid_symbol : in STD_LOGIC; + delay_count : out STD_LOGIC_VECTOR(4 downto 0); + delay_ce : out STD_LOGIC; + bitslip : out STD_LOGIC; + symbol_sync : out STD_LOGIC); + end component; + + signal delay_count : std_logic_vector (4 downto 0); + signal delay_ce : STD_LOGIC; + signal bitslip : STD_LOGIC; + signal symbol_sync_i : STD_LOGIC; + signal symbol : std_logic_vector (9 downto 0); + signal invalid_symbol_i: STD_LOGIC; + +begin + +i_deser: deserialiser_1_to_10 port map ( + clk_mgmt => clk_mgmt, + delay_ce => delay_ce, + delay_count => delay_count, + ce => ce, + clk => clk, + clk_x1 => clk_x1, + bitslip => bitslip, + clk_x5 => clk_x5, + reset => reset, + serial => serial, + data => symbol); + +i_decoder: tmds_decoder port map ( + clk => clk, + symbol => symbol, + invalid_symbol => invalid_symbol_i, + ctl_valid => ctl_valid, + ctl => ctl, + terc4_valid => terc4_valid, + terc4 => terc4, + guardband_valid => guardband_valid, + guardband => guardband, + data_valid => data_valid, + data => data + ); + + invalid_symbol <= invalid_symbol_i; + +i_alignment_detect: alingment_detect port map ( + clk => clk, + invalid_symbol => invalid_symbol_i, + delay_count => delay_count, + delay_ce => delay_ce, + bitslip => bitslip, + symbol_sync => symbol_sync); + +end Behavioral; diff --git a/src/pixel_processing.vhd b/src/pixel_processing.vhd new file mode 100644 index 0000000..e2da145 --- /dev/null +++ b/src/pixel_processing.vhd @@ -0,0 +1,162 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: pixel_processing - Behavioral +-- +-- Description: Where you can do processing on the raw pixel data +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity pixel_processing is + Port ( clk : in STD_LOGIC; + ------------------------------- + -- VGA data recovered from HDMI + ------------------------------- + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_red : in std_logic_vector(7 downto 0); + in_green : in std_logic_vector(7 downto 0); + in_blue : in std_logic_vector(7 downto 0); + ----------------------------------- + -- VGA data to be converted to HDMI + ----------------------------------- + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_red : out std_logic_vector(7 downto 0); + out_green : out std_logic_vector(7 downto 0); + out_blue : out std_logic_vector(7 downto 0); + ------------------------------------ + -- Audio only comes in.. + ------------------------------------ + audio_channel : in std_logic_vector(2 downto 0); + audio_de : in std_logic; + audio_sample : in std_logic_vector(23 downto 0) + + ); +end pixel_processing; + +architecture Behavioral of pixel_processing is + component audio_to_db is + Port ( clk : in STD_LOGIC; + in_channel : in STD_LOGIC_VECTOR (2 downto 0); + in_de : in STD_LOGIC; + in_sample : in STD_LOGIC_VECTOR (23 downto 0); + out_channel : out STD_LOGIC_VECTOR (2 downto 0); + out_de : out STD_LOGIC; + out_level : out STD_LOGIC_VECTOR (5 downto 0)); + end component; + + signal level_channel : std_logic_vector(2 downto 0); + signal level_de : std_logic; + signal level : std_logic_vector(5 downto 0); + + component audio_meters is + Port ( clk : in STD_LOGIC; + ------------------------------- + -- VGA data recovered from HDMI + ------------------------------- + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_red : in std_logic_vector(7 downto 0); + in_green : in std_logic_vector(7 downto 0); + in_blue : in std_logic_vector(7 downto 0); + + ----------------------------------- + -- VGA data to be converted to HDMI + ----------------------------------- + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_red : out std_logic_vector(7 downto 0); + out_green : out std_logic_vector(7 downto 0); + out_blue : out std_logic_vector(7 downto 0); + + ------------------------------------- + -- Audio Levels + ------------------------------------- + signal audio_channel : in std_logic_vector(2 downto 0); + signal audio_de : in std_logic; + signal audio_level : in std_logic_vector(5 downto 0) + ); + end component; + +begin + +i_audio_to_db: audio_to_db port map ( + clk => clk, + + in_channel => audio_channel, + in_de => audio_de, + in_sample => audio_sample, + + out_channel => level_channel, + out_de => level_de, + out_level => level + ); + +i_audio_meters: audio_meters Port map ( + clk => clk, + in_blank => in_blank, + in_hsync => in_hsync, + in_vsync => in_vsync, + in_red => in_red, + in_green => in_green, + in_blue => in_blue, + + out_blank => out_blank, + out_hsync => out_hsync, + out_vsync => out_vsync, + out_red => out_red, + out_green => out_green, + out_blue => out_blue, + + audio_channel => level_channel, + audio_de => level_de, + audio_level => level + ); + + end Behavioral; \ No newline at end of file diff --git a/src/serialiser_10_to_1.vhd b/src/serialiser_10_to_1.vhd new file mode 100644 index 0000000..b10fed5 --- /dev/null +++ b/src/serialiser_10_to_1.vhd @@ -0,0 +1,176 @@ +---------------------------------------------------------------------------------- +-- File: serialiser_10_to_1.vhd +-- +-- Engineer: Mike Field +-- +-- Module Name: serialiser_10_to_1 - Behavioral +-- +-- Description: Using the OSERDESE2 as a 10:1 serialiser, using a x1 and x5 +-- clocks (using DDR outputs). +-- +-- The tricky bit is that reset needs to be asserted, and then CE asserted +-- after the reset or it will not simulate correctly (outputs show as 'X') +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +library UNISIM; +use UNISIM.VComponents.all; + +entity serialiser_10_to_1 is + Port ( clk : in STD_LOGIC; + clk_x5 : in STD_LOGIC; + data : in STD_LOGIC_VECTOR (9 downto 0); + reset : in std_logic; + serial : out STD_LOGIC); +end serialiser_10_to_1; + +architecture Behavioral of serialiser_10_to_1 is + signal shift1 : std_logic := '0'; + signal shift2 : std_logic := '0'; + signal ce_delay : std_logic_vector(7 downto 0) := (others => '0'); + signal reset_delay : std_logic_vector(7 downto 0) := (others => '0'); +begin + +master_serdes : OSERDESE2 + generic map ( + DATA_RATE_OQ => "DDR", -- DDR, SDR + DATA_RATE_TQ => "DDR", -- DDR, BUF, SDR + DATA_WIDTH => 10, -- Parallel data width (2-8,10,14) + INIT_OQ => '1', -- Initial value of OQ output (1'b0,1'b1) + INIT_TQ => '1', -- Initial value of TQ output (1'b0,1'b1) + SERDES_MODE => "MASTER", -- MASTER, SLAVE + SRVAL_OQ => '0', -- OQ output value when SR is used (1'b0,1'b1) + SRVAL_TQ => '0', -- TQ output value when SR is used (1'b0,1'b1) + TBYTE_CTL => "FALSE", -- Enable tristate byte operation (FALSE, TRUE) + TBYTE_SRC => "FALSE", -- Tristate byte source (FALSE, TRUE) + TRISTATE_WIDTH => 1 -- 3-state converter width (1,4) + ) + port map ( + OFB => open, -- 1-bit output: Feedback path for data + OQ => serial, -- 1-bit output: Data path output + -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each) + SHIFTOUT1 => open, + SHIFTOUT2 => open, + TBYTEOUT => open, -- 1-bit output: Byte group tristate + TFB => open, -- 1-bit output: 3-state control + TQ => open, -- 1-bit output: 3-state control + CLK => clk_x5, -- 1-bit input: High speed clock + CLKDIV => clk, -- 1-bit input: Divided clock + -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each) + D1 => data(0), + D2 => data(1), + D3 => data(2), + D4 => data(3), + D5 => data(4), + D6 => data(5), + D7 => data(6), + D8 => data(7), + OCE => '1', --ce_delay(0), -- 1-bit input: Output data clock enable + RST => reset, -- 1-bit input: Reset + -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each) + SHIFTIN1 => SHIFT1, + SHIFTIN2 => SHIFT2, + -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs + T1 => '0', + T2 => '0', + T3 => '0', + T4 => '0', + TBYTEIN => '0', -- 1-bit input: Byte group tristate + TCE => '0' -- 1-bit input: 3-state clock enable + ); + +slave_serdes : OSERDESE2 + generic map ( + DATA_RATE_OQ => "DDR", -- DDR, SDR + DATA_RATE_TQ => "DDR", -- DDR, BUF, SDR + DATA_WIDTH => 10, -- Parallel data width (2-8,10,14) + INIT_OQ => '1', -- Initial value of OQ output (1'b0,1'b1) + INIT_TQ => '1', -- Initial value of TQ output (1'b0,1'b1) + SERDES_MODE => "SLAVE", -- MASTER, SLAVE + SRVAL_OQ => '0', -- OQ output value when SR is used (1'b0,1'b1) + SRVAL_TQ => '0', -- TQ output value when SR is used (1'b0,1'b1) + TBYTE_CTL => "FALSE", -- Enable tristate byte operation (FALSE, TRUE) + TBYTE_SRC => "FALSE", -- Tristate byte source (FALSE, TRUE) + TRISTATE_WIDTH => 1 -- 3-state converter width (1,4) + ) + port map ( + OFB => open, -- 1-bit output: Feedback path for data + OQ => open, -- 1-bit output: Data path output + -- SHIFTOUT1 / SHIFTOUT2: 1-bit (each) output: Data output expansion (1-bit each) + SHIFTOUT1 => shift1, + SHIFTOUT2 => shift2, + + TBYTEOUT => open, -- 1-bit output: Byte group tristate + TFB => open, -- 1-bit output: 3-state control + TQ => open, -- 1-bit output: 3-state control + CLK => clk_x5, -- 1-bit input: High speed clock + CLKDIV => clk, -- 1-bit input: Divided clock + -- D1 - D8: 1-bit (each) input: Parallel data inputs (1-bit each) + D1 => '0', + D2 => '0', + D3 => data(8), + D4 => data(9), + D5 => '0', + D6 => '0', + D7 => '0', + D8 => '0', + OCE => '1', --ce_delay(0), -- 1-bit input: Output data clock enable + RST => reset, -- 1-bit input: Reset + -- SHIFTIN1 / SHIFTIN2: 1-bit (each) input: Data input expansion (1-bit each) + SHIFTIN1 => '0', + SHIFTIN2 => '0', + -- T1 - T4: 1-bit (each) input: Parallel 3-state inputs + T1 => '0', + T2 => '0', + T3 => '0', + T4 => '0', + TBYTEIN => '0', -- 1-bit input: Byte group tristate + TCE => '0' -- 1-bit input: 3-state clock enable + ); + +delay_ce: process(clk_x5) + begin + if rising_edge(clk_x5) then + ce_delay <= not reset & ce_delay(ce_delay'high downto 1); + end if; + end process; +end Behavioral; \ No newline at end of file diff --git a/src/tmds_decoder.vhd b/src/tmds_decoder.vhd new file mode 100644 index 0000000..19b8c8e --- /dev/null +++ b/src/tmds_decoder.vhd @@ -0,0 +1,915 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Create Date: 10.07.2015 20:06:49 +-- Design Name: +-- Module Name: TMDS_decoder - Behavioral +-- +-- Description: Decoding for TMDS encoded symbols. This performs the conversion +-- using a table lookup for simplicity +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.std_logic_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity TMDS_decoder is + Port ( clk : in std_logic; + symbol : in std_logic_vector (9 downto 0); + invalid_symbol : out std_logic; + + ctl_valid : out std_logic; + ctl : out std_logic_vector (1 downto 0); + + terc4_valid : out std_logic; + terc4 : out std_logic_vector (3 downto 0); + + guardband_valid : out std_logic; + guardband : out std_logic_vector (0 downto 0); + + data_valid : out std_logic; + data : out std_logic_vector (7 downto 0)); +end TMDS_decoder; + +architecture Behavioral of TMDS_decoder is + signal lookup : std_logic_vector (8 downto 0); +begin + +decode_ctl: process(clk) + begin + if rising_edge(clk) then + ------------------ + -- TMDS data bytes + if lookup(8) = '1' then + data_valid <= '1'; + data <= lookup(7 downto 0); + else + data_valid <= '0'; + end if; + + ------------ + -- CTL codes + if lookup(8 downto 7) = "01" then + ctl_valid <= '1'; + ctl <= lookup(1 downto 0); + else + ctl_valid <= '0'; + end if; + + ------------------------------ + -- All other codes are invalid + ------------------------------ + if lookup(8 downto 7) = "00" then + invalid_symbol <= '1'; + else + invalid_symbol <= '0'; + end if; + + terc4_valid <= '0'; + guardband_valid <= '0'; + if lookup(8) = '1' then + ------------------------- + -- Decode the guard bands + ------------------------- + case lookup(7 downto 0) is + when x"55" => guardband_valid <= '1'; guardband <= "0"; + when x"AB" => guardband_valid <= '1'; guardband <= "1"; + when others => null; + end case; + + ------------------------- + -- Decode TERC4 data + ------------------------- + case lookup(7 downto 0) is + when x"5B" => terc4_valid <= '1'; terc4 <= "0000";-- "1010011100" TERC4 0000 + when x"5A" => terc4_valid <= '1'; terc4 <= "0001"; -- "1001100011" TERC4 0001 + when x"D3" => terc4_valid <= '1'; terc4 <= "0010"; -- "1011100100" TERC4 0010 + when x"D9" => terc4_valid <= '1'; terc4 <= "0011"; -- "1011100010" TERC4 0011 + when x"93" => terc4_valid <= '1'; terc4 <= "0100"; -- "0101110001" TERC4 0100 + when x"22" => terc4_valid <= '1'; terc4 <= "0101"; -- "0100011110" TERC4 0101 + when x"92" => terc4_valid <= '1'; terc4 <= "0110"; -- "0110001110" TERC4 0110 + when x"44" => terc4_valid <= '1'; terc4 <= "0110"; -- "0100111100" TERC4 0111 + when x"AB" => terc4_valid <= '1'; terc4 <= "1000"; -- "1011001100" TERC4 1000 & HDMI Guard band (video C0 and Video C2) + when x"4B" => terc4_valid <= '1'; terc4 <= "1001"; -- "0100111001" TERC4 1001 + when x"A4" => terc4_valid <= '1'; terc4 <= "1010"; -- "0110011100" TERC4 1010 + when x"D5" => terc4_valid <= '1'; terc4 <= "1011"; -- "1011000110" TERC4 1011 + when x"6D" => terc4_valid <= '1'; terc4 <= "1100"; -- "1010001110" TERC4 1100 + when x"6C" => terc4_valid <= '1'; terc4 <= "1101"; -- "1001110001" TERC4 1101 + when x"A5" => terc4_valid <= '1'; terc4 <= "1110"; -- "0101100011" TERC4 1110 + when x"BA" => terc4_valid <= '1'; terc4 <= "1111"; -- "1011000011" TERC4 1111 + when others => null; + end case; + end if; + + ------------------------------------------------------------- + -- Convert the incoming signal to something we can decode + -- + -- For data symbols + -- ---------------- + -- bit 8 - 1 -- Data word flage + -- bits 7:0 - xxxxxxxx - Data value + -- + -- For CTL symbols + -- --------------- + -- bit 8 - 0 - Data word flage + -- bit 7 - 1 - CTL Indicator + -- bits 6:2 - X - Ignored + -- bits 1:0 - xx - CTL value + -- + -- For Invalid symbols + -- ------------------- + -- bit 8 - 0 - Data word flage + -- bit 7 - 0 - TERC4 Inicated + -- bit 6 - 0 - CTL Indicator + -- bit 5 - 0 - Guard band indicator + -- bits 4:0 - X - Unused + -- + ------------------------------------------------------------- + case symbol is + -- DVI-D Data sybmols + -- Data 00 + when "1111111111" => lookup <= "100000000"; + when "0100000000" => lookup <= "100000000"; + -- Data 01 + when "0111111111" => lookup <= "100000001"; + when "1100000000" => lookup <= "100000001"; + -- Data 02 + when "0111111110" => lookup <= "100000010"; + when "1100000001" => lookup <= "100000010"; + -- Data 03 + when "1111111110" => lookup <= "100000011"; + when "0100000001" => lookup <= "100000011"; + -- Data 04 + when "0111111100" => lookup <= "100000100"; + when "1100000011" => lookup <= "100000100"; + -- Data 05 + when "1111111100" => lookup <= "100000101"; + when "0100000011" => lookup <= "100000101"; + -- Data 06 + when "1111111101" => lookup <= "100000110"; + when "0100000010" => lookup <= "100000110"; + -- Data 07 + when "0111111101" => lookup <= "100000111"; + when "1100000010" => lookup <= "100000111"; + -- Data 08 + when "0111111000" => lookup <= "100001000"; + when "1100000111" => lookup <= "100001000"; + -- Data 09 + when "1111111000" => lookup <= "100001001"; + when "0100000111" => lookup <= "100001001"; + -- Data 0a + when "1111111001" => lookup <= "100001010"; + when "0100000110" => lookup <= "100001010"; + -- Data 0b + when "0111111001" => lookup <= "100001011"; + when "1100000110" => lookup <= "100001011"; + -- Data 0c + when "1111111011" => lookup <= "100001100"; + when "0100000100" => lookup <= "100001100"; + -- Data 0d + when "0111111011" => lookup <= "100001101"; + when "1100000100" => lookup <= "100001101"; + -- Data 0e + when "0111111010" => lookup <= "100001110"; + when "1100000101" => lookup <= "100001110"; + -- Data 0f + when "1111111010" => lookup <= "100001111"; + when "0100000101" => lookup <= "100001111"; + -- Data 10 + when "0111110000" => lookup <= "100010000"; + -- Data 11 + when "0100001111" => lookup <= "100010001"; + -- Data 12 + when "1111110001" => lookup <= "100010010"; + when "0100001110" => lookup <= "100010010"; + -- Data 13 + when "0111110001" => lookup <= "100010011"; + when "1100001110" => lookup <= "100010011"; + -- Data 14 + when "1111110011" => lookup <= "100010100"; + when "0100001100" => lookup <= "100010100"; + -- Data 15 + when "0111110011" => lookup <= "100010101"; + when "1100001100" => lookup <= "100010101"; + -- Data 16 + when "0111110010" => lookup <= "100010110"; + when "1100001101" => lookup <= "100010110"; + -- Data 17 + when "1111110010" => lookup <= "100010111"; + when "0100001101" => lookup <= "100010111"; + -- Data 18 + when "1111110111" => lookup <= "100011000"; + when "0100001000" => lookup <= "100011000"; + -- Data 19 + when "0111110111" => lookup <= "100011001"; + when "1100001000" => lookup <= "100011001"; + -- Data 1a + when "0111110110" => lookup <= "100011010"; + when "1100001001" => lookup <= "100011010"; + -- Data 1b + when "1111110110" => lookup <= "100011011"; + when "0100001001" => lookup <= "100011011"; + -- Data 1c + when "0111110100" => lookup <= "100011100"; + when "1100001011" => lookup <= "100011100"; + -- Data 1d + when "1111110100" => lookup <= "100011101"; + when "0100001011" => lookup <= "100011101"; + -- Data 1e + when "1001011111" => lookup <= "100011110"; + when "0010100000" => lookup <= "100011110"; + -- Data 1f + when "0001011111" => lookup <= "100011111"; + when "1010100000" => lookup <= "100011111"; + -- Data 20 + when "1100011111" => lookup <= "100100000"; + when "0111100000" => lookup <= "100100000"; + -- Data 21 + when "0100011111" => lookup <= "100100001"; + when "1111100000" => lookup <= "100100001"; + -- Data 22 + when "0100011110" => lookup <= "100100010"; -- TERC4 0101 + -- Data 23 + when "0111100001" => lookup <= "100100011"; + -- Data 24 + when "1111100011" => lookup <= "100100100"; + when "0100011100" => lookup <= "100100100"; + -- Data 25 + when "0111100011" => lookup <= "100100101"; + when "1100011100" => lookup <= "100100101"; + -- Data 26 + when "0111100010" => lookup <= "100100110"; + -- Data 27 + when "0100011101" => lookup <= "100100111"; + -- Data 28 + when "1111100111" => lookup <= "100101000"; + when "0100011000" => lookup <= "100101000"; + -- Data 29 + when "0111100111" => lookup <= "100101001"; + when "1100011000" => lookup <= "100101001"; + -- Data 2a + when "0111100110" => lookup <= "100101010"; + when "1100011001" => lookup <= "100101010"; + -- Data 2b + when "1111100110" => lookup <= "100101011"; + when "0100011001" => lookup <= "100101011"; + -- Data 2c + when "0111100100" => lookup <= "100101100"; + -- Data 2d + when "0100011011" => lookup <= "100101101"; + -- Data 2e + when "1001001111" => lookup <= "100101110"; + when "0010110000" => lookup <= "100101110"; + -- Data 2f + when "0001001111" => lookup <= "100101111"; + when "1010110000" => lookup <= "100101111"; + -- Data 30 + when "1111101111" => lookup <= "100110000"; + when "0100010000" => lookup <= "100110000"; + -- Data 31 + when "0111101111" => lookup <= "100110001"; + when "1100010000" => lookup <= "100110001"; + -- Data 32 + when "0111101110" => lookup <= "100110010"; + when "1100010001" => lookup <= "100110010"; + -- Data 33 + when "1111101110" => lookup <= "100110011"; + when "0100010001" => lookup <= "100110011"; + -- Data 34 + when "0111101100" => lookup <= "100110100"; + when "1100010011" => lookup <= "100110100"; + -- Data 35 + when "1111101100" => lookup <= "100110101"; + when "0100010011" => lookup <= "100110101"; + -- Data 36 + when "1001000111" => lookup <= "100110110"; + -- Data 37 + when "1010111000" => lookup <= "100110111"; + -- Data 38 + when "0111101000" => lookup <= "100111000"; + -- Data 39 + when "0100010111" => lookup <= "100111001"; + -- Data 3a + when "0010111100" => lookup <= "100111010"; + when "1001000011" => lookup <= "100111010"; + -- Data 3b + when "1010111100" => lookup <= "100111011"; + when "0001000011" => lookup <= "100111011"; + -- Data 3c + when "0010111110" => lookup <= "100111100"; + when "1001000001" => lookup <= "100111100"; + -- Data 3d + when "1010111110" => lookup <= "100111101"; + when "0001000001" => lookup <= "100111101"; + -- Data 3e + when "1010111111" => lookup <= "100111110"; + when "0001000000" => lookup <= "100111110"; + -- Data 3f + when "0010111111" => lookup <= "100111111"; + when "1001000000" => lookup <= "100111111"; + -- Data 40 + when "1100111111" => lookup <= "101000000"; + when "0111000000" => lookup <= "101000000"; + -- Data 41 + when "0100111111" => lookup <= "101000001"; + when "1111000000" => lookup <= "101000001"; + -- Data 42 + when "0100111110" => lookup <= "101000010"; + when "1111000001" => lookup <= "101000010"; + -- Data 43 + when "1100111110" => lookup <= "101000011"; + when "0111000001" => lookup <= "101000011"; + -- Data 44 + when "0100111100" => lookup <= "101000100"; -- TERC4 0111 + -- Data 45 + when "0111000011" => lookup <= "101000101"; + -- Data 46 + when "1100111101" => lookup <= "101000110"; + when "0111000010" => lookup <= "101000110"; + -- Data 47 + when "0100111101" => lookup <= "101000111"; + when "1111000010" => lookup <= "101000111"; + -- Data 48 + when "1111000111" => lookup <= "101001000"; + when "0100111000" => lookup <= "101001000"; + -- Data 49 + when "0111000111" => lookup <= "101001001"; + when "1100111000" => lookup <= "101001001"; + -- Data 4a + when "0111000110" => lookup <= "101001010"; + -- Data 4b + when "0100111001" => lookup <= "101001011"; -- TERC4 1001 + -- Data 4c + when "1100111011" => lookup <= "101001100"; + when "0111000100" => lookup <= "101001100"; + -- Data 4d + when "0100111011" => lookup <= "101001101"; + when "1111000100" => lookup <= "101001101"; + -- Data 4e + when "1001101111" => lookup <= "101001110"; + when "0010010000" => lookup <= "101001110"; + -- Data 4f + when "0001101111" => lookup <= "101001111"; + when "1010010000" => lookup <= "101001111"; + -- Data 50 + when "1111001111" => lookup <= "101010000"; + when "0100110000" => lookup <= "101010000"; + -- Data 51 + when "0111001111" => lookup <= "101010001"; + when "1100110000" => lookup <= "101010001"; + -- Data 52 + when "0111001110" => lookup <= "101010010"; + when "1100110001" => lookup <= "101010010"; + -- Data 53 + when "1111001110" => lookup <= "101010011"; + when "0100110001" => lookup <= "101010011"; + -- Data 54 + when "0111001100" => lookup <= "101010100"; + -- Data 55 + when "0100110011" => lookup <= "101010101"; -- HDMI Guard band (video C1, data C1 & C2) + -- Data 56 + when "1001100111" => lookup <= "101010110"; + when "0010011000" => lookup <= "101010110"; + -- Data 57 + when "0001100111" => lookup <= "101010111"; + when "1010011000" => lookup <= "101010111"; + -- Data 58 + when "1100110111" => lookup <= "101011000"; + when "0111001000" => lookup <= "101011000"; + -- Data 59 + when "0100110111" => lookup <= "101011001"; + when "1111001000" => lookup <= "101011001"; + -- Data 5a + when "1001100011" => lookup <= "101011010"; -- TERC4 0001 + -- Data 5b + when "1010011100" => lookup <= "101011011"; -- TERC4 0000 + -- Data 5c + when "0010011110" => lookup <= "101011100"; + when "1001100001" => lookup <= "101011100"; + -- Data 5d + when "1010011110" => lookup <= "101011101"; + when "0001100001" => lookup <= "101011101"; + -- Data 5e + when "1010011111" => lookup <= "101011110"; + when "0001100000" => lookup <= "101011110"; + -- Data 5f + when "0010011111" => lookup <= "101011111"; + when "1001100000" => lookup <= "101011111"; + -- Data 60 + when "1111011111" => lookup <= "101100000"; + when "0100100000" => lookup <= "101100000"; + -- Data 61 + when "0111011111" => lookup <= "101100001"; + when "1100100000" => lookup <= "101100001"; + -- Data 62 + when "0111011110" => lookup <= "101100010"; + when "1100100001" => lookup <= "101100010"; + -- Data 63 + when "1111011110" => lookup <= "101100011"; + when "0100100001" => lookup <= "101100011"; + -- Data 64 + when "0111011100" => lookup <= "101100100"; + when "1100100011" => lookup <= "101100100"; + -- Data 65 + when "1111011100" => lookup <= "101100101"; + when "0100100011" => lookup <= "101100101"; + -- Data 66 + when "1001110111" => lookup <= "101100110"; + when "0010001000" => lookup <= "101100110"; + -- Data 67 + when "0001110111" => lookup <= "101100111"; + when "1010001000" => lookup <= "101100111"; + -- Data 68 + when "0111011000" => lookup <= "101101000"; + -- Data 69 + when "0100100111" => lookup <= "101101001"; + -- Data 6a + when "1001110011" => lookup <= "101101010"; + when "0010001100" => lookup <= "101101010"; + -- Data 6b + when "0001110011" => lookup <= "101101011"; + when "1010001100" => lookup <= "101101011"; + -- Data 6c + when "1001110001" => lookup <= "101101100"; -- TERC4 1101 + -- Data 6d + when "1010001110" => lookup <= "101101101"; -- TERC4 1100 + -- Data 6e + when "1010001111" => lookup <= "101101110"; + when "0001110000" => lookup <= "101101110"; + -- Data 6f + when "0010001111" => lookup <= "101101111"; + when "1001110000" => lookup <= "101101111"; + -- Data 70 + when "1100101111" => lookup <= "101110000"; + when "0111010000" => lookup <= "101110000"; + -- Data 71 + when "0100101111" => lookup <= "101110001"; + when "1111010000" => lookup <= "101110001"; + -- Data 72 + when "1001111011" => lookup <= "101110010"; + when "0010000100" => lookup <= "101110010"; + -- Data 73 + when "0001111011" => lookup <= "101110011"; + when "1010000100" => lookup <= "101110011"; + -- Data 74 + when "1001111001" => lookup <= "101110100"; + when "0010000110" => lookup <= "101110100"; + -- Data 75 + when "0001111001" => lookup <= "101110101"; + when "1010000110" => lookup <= "101110101"; + -- Data 76 + when "1010000111" => lookup <= "101110110"; + -- Data 77 + when "1001111000" => lookup <= "101110111"; + -- Data 78 + when "1001111101" => lookup <= "101111000"; + when "0010000010" => lookup <= "101111000"; + -- Data 79 + when "0001111101" => lookup <= "101111001"; + when "1010000010" => lookup <= "101111001"; + -- Data 7a + when "0001111100" => lookup <= "101111010"; + when "1010000011" => lookup <= "101111010"; + -- Data 7b + when "1001111100" => lookup <= "101111011"; + when "0010000011" => lookup <= "101111011"; + -- Data 7c + when "0001111110" => lookup <= "101111100"; + when "1010000001" => lookup <= "101111100"; + -- Data 7d + when "1001111110" => lookup <= "101111101"; + when "0010000001" => lookup <= "101111101"; + -- Data 7e + when "1001111111" => lookup <= "101111110"; + when "0010000000" => lookup <= "101111110"; + -- Data 7f + when "0001111111" => lookup <= "101111111"; + when "1010000000" => lookup <= "101111111"; + -- Data 80 + when "1101111111" => lookup <= "110000000"; + when "0110000000" => lookup <= "110000000"; + -- Data 81 + when "0101111111" => lookup <= "110000001"; + when "1110000000" => lookup <= "110000001"; + -- Data 82 + when "0101111110" => lookup <= "110000010"; + when "1110000001" => lookup <= "110000010"; + -- Data 83 + when "1101111110" => lookup <= "110000011"; + when "0110000001" => lookup <= "110000011"; + -- Data 84 + when "0101111100" => lookup <= "110000100"; + when "1110000011" => lookup <= "110000100"; + -- Data 85 + when "1101111100" => lookup <= "110000101"; + when "0110000011" => lookup <= "110000101"; + -- Data 86 + when "1101111101" => lookup <= "110000110"; + when "0110000010" => lookup <= "110000110"; + -- Data 87 + when "0101111101" => lookup <= "110000111"; + when "1110000010" => lookup <= "110000111"; + -- Data 88 + when "0101111000" => lookup <= "110001000"; + -- Data 89 + when "0110000111" => lookup <= "110001001"; + -- Data 8a + when "1101111001" => lookup <= "110001010"; + when "0110000110" => lookup <= "110001010"; + -- Data 8b + when "0101111001" => lookup <= "110001011"; + when "1110000110" => lookup <= "110001011"; + -- Data 8c + when "1101111011" => lookup <= "110001100"; + when "0110000100" => lookup <= "110001100"; + -- Data 8d + when "0101111011" => lookup <= "110001101"; + when "1110000100" => lookup <= "110001101"; + -- Data 8e + when "1000101111" => lookup <= "110001110"; + when "0011010000" => lookup <= "110001110"; + -- Data 8f + when "0000101111" => lookup <= "110001111"; + when "1011010000" => lookup <= "110001111"; + -- Data 90 + when "1110001111" => lookup <= "110010000"; + when "0101110000" => lookup <= "110010000"; + -- Data 91 + when "0110001111" => lookup <= "110010001"; + when "1101110000" => lookup <= "110010001"; + -- Data 92 + when "0110001110" => lookup <= "110010010"; -- TERC4 0110 + -- Data 93 + when "0101110001" => lookup <= "110010011"; -- TERC4 0100 + -- Data 94 + when "1101110011" => lookup <= "110010100"; + when "0110001100" => lookup <= "110010100"; + -- Data 95 + when "0101110011" => lookup <= "110010101"; + when "1110001100" => lookup <= "110010101"; + -- Data 96 + when "1000100111" => lookup <= "110010110"; + -- Data 97 + when "1011011000" => lookup <= "110010111"; + -- Data 98 + when "1101110111" => lookup <= "110011000"; + when "0110001000" => lookup <= "110011000"; + -- Data 99 + when "0101110111" => lookup <= "110011001"; + when "1110001000" => lookup <= "110011001"; + -- Data 9a + when "0011011100" => lookup <= "110011010"; + when "1000100011" => lookup <= "110011010"; + -- Data 9b + when "1011011100" => lookup <= "110011011"; + when "0000100011" => lookup <= "110011011"; + -- Data 9c + when "0011011110" => lookup <= "110011100"; + when "1000100001" => lookup <= "110011100"; + -- Data 9d + when "1011011110" => lookup <= "110011101"; + when "0000100001" => lookup <= "110011101"; + -- Data 9e + when "1011011111" => lookup <= "110011110"; + when "0000100000" => lookup <= "110011110"; + -- Data 9f + when "0011011111" => lookup <= "110011111"; + when "1000100000" => lookup <= "110011111"; + -- Data a0 + when "1110011111" => lookup <= "110100000"; + when "0101100000" => lookup <= "110100000"; + -- Data a1 + when "0110011111" => lookup <= "110100001"; + when "1101100000" => lookup <= "110100001"; + -- Data a2 + when "0110011110" => lookup <= "110100010"; + when "1101100001" => lookup <= "110100010"; + -- Data a3 + when "1110011110" => lookup <= "110100011"; + when "0101100001" => lookup <= "110100011"; + -- Data a4 + when "0110011100" => lookup <= "110100100"; -- TERC4 1010 + -- Data a5 + when "0101100011" => lookup <= "110100101"; -- TERC4 1110 + -- Data a6 + when "1000110111" => lookup <= "110100110"; + when "0011001000" => lookup <= "110100110"; + -- Data a7 + when "0000110111" => lookup <= "110100111"; + when "1011001000" => lookup <= "110100111"; + -- Data a8 + when "1101100111" => lookup <= "110101000"; + when "0110011000" => lookup <= "110101000"; + -- Data a9 + when "0101100111" => lookup <= "110101001"; + when "1110011000" => lookup <= "110101001"; + -- Data aa + when "1000110011" => lookup <= "110101010"; + -- Data ab + when "1011001100" => lookup <= "110101011"; -- TERC4 1000 & HDMI Guard band (video C0 and Video C2) + -- Data ac + when "0011001110" => lookup <= "110101100"; + when "1000110001" => lookup <= "110101100"; + -- Data ad + when "1011001110" => lookup <= "110101101"; + when "0000110001" => lookup <= "110101101"; + -- Data ae + when "1011001111" => lookup <= "110101110"; + when "0000110000" => lookup <= "110101110"; + -- Data af + when "0011001111" => lookup <= "110101111"; + when "1000110000" => lookup <= "110101111"; + -- Data b0 + when "1101101111" => lookup <= "110110000"; + when "0110010000" => lookup <= "110110000"; + -- Data b1 + when "0101101111" => lookup <= "110110001"; + when "1110010000" => lookup <= "110110001"; + -- Data b2 + when "1000111011" => lookup <= "110110010"; + when "0011000100" => lookup <= "110110010"; + -- Data b3 + when "0000111011" => lookup <= "110110011"; + when "1011000100" => lookup <= "110110011"; + -- Data b4 + when "1000111001" => lookup <= "110110100"; + -- Data b5 + when "1011000110" => lookup <= "110110101"; -- TERC4 1011 + -- Data b6 + when "1011000111" => lookup <= "110110110"; + when "0000111000" => lookup <= "110110110"; + -- Data b7 + when "0011000111" => lookup <= "110110111"; + when "1000111000" => lookup <= "110110111"; + -- Data b8 + when "1000111101" => lookup <= "110111000"; + when "0011000010" => lookup <= "110111000"; + -- Data b9 + when "0000111101" => lookup <= "110111001"; + when "1011000010" => lookup <= "110111001"; + -- Data ba + when "1011000011" => lookup <= "110111010"; -- TERC4 1111 + -- Data bb + when "1000111100" => lookup <= "110111011"; + -- Data bc + when "0000111110" => lookup <= "110111100"; + when "1011000001" => lookup <= "110111100"; + -- Data bd + when "1000111110" => lookup <= "110111101"; + when "0011000001" => lookup <= "110111101"; + -- Data be + when "1000111111" => lookup <= "110111110"; + when "0011000000" => lookup <= "110111110"; + -- Data bf + when "0000111111" => lookup <= "110111111"; + when "1011000000" => lookup <= "110111111"; + -- Data c0 + when "1110111111" => lookup <= "111000000"; + when "0101000000" => lookup <= "111000000"; + -- Data c1 + when "0110111111" => lookup <= "111000001"; + when "1101000000" => lookup <= "111000001"; + -- Data c2 + when "0110111110" => lookup <= "111000010"; + when "1101000001" => lookup <= "111000010"; + -- Data c3 + when "1110111110" => lookup <= "111000011"; + when "0101000001" => lookup <= "111000011"; + -- Data c4 + when "0110111100" => lookup <= "111000100"; + when "1101000011" => lookup <= "111000100"; + -- Data c5 + when "1110111100" => lookup <= "111000101"; + when "0101000011" => lookup <= "111000101"; + -- Data c6 + when "1000010111" => lookup <= "111000110"; + -- Data c7 + when "1011101000" => lookup <= "111000111"; + -- Data c8 + when "0110111000" => lookup <= "111001000"; + -- Data c9 + when "0101000111" => lookup <= "111001001"; + -- Data ca + when "0011101100" => lookup <= "111001010"; + when "1000010011" => lookup <= "111001010"; + -- Data cb + when "1011101100" => lookup <= "111001011"; + when "0000010011" => lookup <= "111001011"; + -- Data cc + when "0011101110" => lookup <= "111001100"; + when "1000010001" => lookup <= "111001100"; + -- Data cd + when "1011101110" => lookup <= "111001101"; + when "0000010001" => lookup <= "111001101"; + -- Data ce + when "1011101111" => lookup <= "111001110"; + when "0000010000" => lookup <= "111001110"; + -- Data cf + when "0011101111" => lookup <= "111001111"; + when "1000010000" => lookup <= "111001111"; + -- Data d0 + when "1101001111" => lookup <= "111010000"; + when "0110110000" => lookup <= "111010000"; + -- Data d1 + when "0101001111" => lookup <= "111010001"; + when "1110110000" => lookup <= "111010001"; + -- Data d2 + when "1000011011" => lookup <= "111010010"; + -- Data d3 + when "1011100100" => lookup <= "111010011"; -- TERC4 0010 + -- Data d4 + when "0011100110" => lookup <= "111010100"; + when "1000011001" => lookup <= "111010100"; + -- Data d5 + when "1011100110" => lookup <= "111010101"; + when "0000011001" => lookup <= "111010101"; + -- Data d6 + when "1011100111" => lookup <= "111010110"; + when "0000011000" => lookup <= "111010110"; + -- Data d7 + when "0011100111" => lookup <= "111010111"; + when "1000011000" => lookup <= "111010111"; + -- Data d8 + when "1000011101" => lookup <= "111011000"; + -- Data d9 + when "1011100010" => lookup <= "111011001"; -- TERC4 0011 + -- Data da + when "1011100011" => lookup <= "111011010"; + when "0000011100" => lookup <= "111011010"; + -- Data db + when "0011100011" => lookup <= "111011011"; + when "1000011100" => lookup <= "111011011"; + -- Data dc + when "1011100001" => lookup <= "111011100"; + -- Data dd + when "1000011110" => lookup <= "111011101"; + -- Data de + when "1000011111" => lookup <= "111011110"; + when "0011100000" => lookup <= "111011110"; + -- Data df + when "0000011111" => lookup <= "111011111"; + when "1011100000" => lookup <= "111011111"; + -- Data e0 + when "1101011111" => lookup <= "111100000"; + when "0110100000" => lookup <= "111100000"; + -- Data e1 + when "0101011111" => lookup <= "111100001"; + when "1110100000" => lookup <= "111100001"; + -- Data e2 + when "0011110100" => lookup <= "111100010"; + when "1000001011" => lookup <= "111100010"; + -- Data e3 + when "1011110100" => lookup <= "111100011"; + when "0000001011" => lookup <= "111100011"; + -- Data e4 + when "0011110110" => lookup <= "111100100"; + when "1000001001" => lookup <= "111100100"; + -- Data e5 + when "1011110110" => lookup <= "111100101"; + when "0000001001" => lookup <= "111100101"; + -- Data e6 + when "1011110111" => lookup <= "111100110"; + when "0000001000" => lookup <= "111100110"; + -- Data e7 + when "0011110111" => lookup <= "111100111"; + when "1000001000" => lookup <= "111100111"; + -- Data e8 + when "0011110010" => lookup <= "111101000"; + when "1000001101" => lookup <= "111101000"; + -- Data e9 + when "1011110010" => lookup <= "111101001"; + when "0000001101" => lookup <= "111101001"; + -- Data ea + when "1011110011" => lookup <= "111101010"; + when "0000001100" => lookup <= "111101010"; + -- Data eb + when "0011110011" => lookup <= "111101011"; + when "1000001100" => lookup <= "111101011"; + -- Data ec + when "1011110001" => lookup <= "111101100"; + when "0000001110" => lookup <= "111101100"; + -- Data ed + when "0011110001" => lookup <= "111101101"; + when "1000001110" => lookup <= "111101101"; + -- Data ee + when "1000001111" => lookup <= "111101110"; + -- Data ef + when "1011110000" => lookup <= "111101111"; + -- Data f0 + when "0011111010" => lookup <= "111110000"; + when "1000000101" => lookup <= "111110000"; + -- Data f1 + when "1011111010" => lookup <= "111110001"; + when "0000000101" => lookup <= "111110001"; + -- Data f2 + when "1011111011" => lookup <= "111110010"; + when "0000000100" => lookup <= "111110010"; + -- Data f3 + when "0011111011" => lookup <= "111110011"; + when "1000000100" => lookup <= "111110011"; + -- Data f4 + when "1011111001" => lookup <= "111110100"; + when "0000000110" => lookup <= "111110100"; + -- Data f5 + when "0011111001" => lookup <= "111110101"; + when "1000000110" => lookup <= "111110101"; + -- Data f6 + when "0011111000" => lookup <= "111110110"; + when "1000000111" => lookup <= "111110110"; + -- Data f7 + when "1011111000" => lookup <= "111110111"; + when "0000000111" => lookup <= "111110111"; + -- Data f8 + when "1011111101" => lookup <= "111111000"; + when "0000000010" => lookup <= "111111000"; + -- Data f9 + when "0011111101" => lookup <= "111111001"; + when "1000000010" => lookup <= "111111001"; + -- Data fa + when "0011111100" => lookup <= "111111010"; + when "1000000011" => lookup <= "111111010"; + -- Data fb + when "1011111100" => lookup <= "111111011"; + when "0000000011" => lookup <= "111111011"; + -- Data fc + when "0011111110" => lookup <= "111111100"; + when "1000000001" => lookup <= "111111100"; + -- Data fd + when "1011111110" => lookup <= "111111101"; + when "0000000001" => lookup <= "111111101"; + -- Data fe + when "1011111111" => lookup <= "111111110"; + when "0000000000" => lookup <= "111111110"; + -- Data ff + when "0011111111" => lookup <= "111111111"; + when "1000000000" => lookup <= "111111111"; + + -- DVI-D CTL symbols + when "0010101011" => lookup <= "01" & "00000" & "01"; -- CTL1 + when "0101010100" => lookup <= "01" & "00000" & "10"; -- CTL2 + when "1010101011" => lookup <= "01" & "00000" & "11"; -- CTL3 + when "1101010100" => lookup <= "01" & "00000" & "00"; -- CTL0 + + -- Invalid symbols + when others => lookup <= "0000" & "00000"; + end case; + end if; + end process; +end Behavioral; + +-- For Guard band and TERC4 decoding (to be done later!) +-- when x"55" => -- "0100110011" HDMI Guard band (video C1, data C1 & C2) +-- when x"5B" => -- "1010011100" TERC4 0000 +-- when x"5A" => -- "1001100011" TERC4 0001 +-- when x"D3" => -- "1011100100" TERC4 0010 +-- when x"D9" => -- "1011100010" TERC4 0011 +-- when x"93" => -- "0101110001" TERC4 0100 +-- when x"22" => -- "0100011110" TERC4 0101 +-- when x"92" => -- "0110001110" TERC4 0110 +-- when x"44" => -- "0100111100" TERC4 0111 +-- when x"AB" => -- "1011001100" TERC4 1000 & HDMI Guard band (video C0 and Video C2) +-- when x"4B" => -- "0100111001" TERC4 1001 +-- when x"A4" => -- "0110011100" TERC4 1010 +-- when x"B5" => -- "1011000110" TERC4 1011 +-- when x"6D" => -- "1010001110" TERC4 1100 +-- when x"6C" => -- "1001110001" TERC4 1101 +-- when x"A5" => -- "0101100011" TERC4 1110 +-- when x"BA" => -- "1011000011" TERC4 1111 \ No newline at end of file diff --git a/src/tmds_encoder.vhd b/src/tmds_encoder.vhd new file mode 100644 index 0000000..539bf4a --- /dev/null +++ b/src/tmds_encoder.vhd @@ -0,0 +1,144 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: tmds_encoder - Behavioral +-- +-- Description: 8b/10b TMDS encoder +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.STD_LOGIC_UNSIGNED.ALL; + +entity tmds_encoder is + Port ( clk : in std_logic; + data : in std_logic_vector (7 downto 0); + c : in std_logic_vector (1 downto 0); + blank : in std_logic; + encoded : out std_logic_vector (9 downto 0)); +end entity; + +architecture Behavioral of tmds_encoder is + signal xored : STD_LOGIC_VECTOR (8 downto 0); + signal xnored : STD_LOGIC_VECTOR (8 downto 0); + + signal ones : STD_LOGIC_VECTOR (3 downto 0); + signal data_word : STD_LOGIC_VECTOR (8 downto 0); + signal data_word_inv : STD_LOGIC_VECTOR (8 downto 0); + signal data_word_disparity : STD_LOGIC_VECTOR (3 downto 0); + signal dc_bias : STD_LOGIC_VECTOR (3 downto 0) := (others => '0'); +begin + -- Work our the two different encodings for the byte + xored(0) <= data(0); + xored(1) <= data(1) xor xored(0); + xored(2) <= data(2) xor xored(1); + xored(3) <= data(3) xor xored(2); + xored(4) <= data(4) xor xored(3); + xored(5) <= data(5) xor xored(4); + xored(6) <= data(6) xor xored(5); + xored(7) <= data(7) xor xored(6); + xored(8) <= '1'; + + xnored(0) <= data(0); + xnored(1) <= data(1) xnor xnored(0); + xnored(2) <= data(2) xnor xnored(1); + xnored(3) <= data(3) xnor xnored(2); + xnored(4) <= data(4) xnor xnored(3); + xnored(5) <= data(5) xnor xnored(4); + xnored(6) <= data(6) xnor xnored(5); + xnored(7) <= data(7) xnor xnored(6); + xnored(8) <= '0'; + + -- Count how many ones are set in data + ones <= "0000" + data(0) + data(1) + data(2) + data(3) + + data(4) + data(5) + data(6) + data(7); + +-- Decide which encoding to use +process(ones, data(0), xnored, xored) +begin + if ones > 4 or (ones = 4 and data(0) = '0') then + data_word <= xnored; + data_word_inv <= NOT(xnored); + else + data_word <= xored; + data_word_inv <= NOT(xored); + end if; +end process; + +-- Work out the DC bias of the dataword; +data_word_disparity <= "1100" + data_word(0) + data_word(1) + data_word(2) + data_word(3) + + data_word(4) + data_word(5) + data_word(6) + data_word(7); + +-- Now work out what the output should be +process(clk) + begin + if rising_edge(clk) then + if blank = '1' then + -- In the control periods, all values have and have balanced bit count + case c is + when "00" => encoded <= "1101010100"; + when "01" => encoded <= "0010101011"; + when "10" => encoded <= "0101010100"; + when others => encoded <= "1010101011"; + end case; + dc_bias <= (others => '0'); + else + if dc_bias = "00000" or data_word_disparity = 0 then + -- dataword has no disparity + if data_word(8) = '1' then + encoded <= "01" & data_word(7 downto 0); + dc_bias <= dc_bias + data_word_disparity; + else + encoded <= "10" & data_word_inv(7 downto 0); + dc_bias <= dc_bias - data_word_disparity; + end if; + elsif (dc_bias(3) = '0' and data_word_disparity(3) = '0') or + (dc_bias(3) = '1' and data_word_disparity(3) = '1') then + encoded <= '1' & data_word(8) & data_word_inv(7 downto 0); + dc_bias <= dc_bias + data_word(8) - data_word_disparity; + else + encoded <= '0' & data_word; + dc_bias <= dc_bias - data_word_inv(8) + data_word_disparity; + end if; + end if; + end if; + end process; +end Behavioral; \ No newline at end of file diff --git a/test_bench/hdmi_test_generator/hdmi_ouput_test.vhd b/test_bench/hdmi_test_generator/hdmi_ouput_test.vhd new file mode 100644 index 0000000..a275682 --- /dev/null +++ b/test_bench/hdmi_test_generator/hdmi_ouput_test.vhd @@ -0,0 +1,148 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Top level design for my minimal HDMI output project +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +----------------------------------------------------------------------------------library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity hdmi_output_test is + Port ( clk50 : in STD_LOGIC; + + hdmi_out_p : out STD_LOGIC_VECTOR(3 downto 0); + hdmi_out_n : out STD_LOGIC_VECTOR(3 downto 0); + + leds : out std_logic_vector(7 downto 0)); +end hdmi_output_test; + +architecture Behavioral of hdmi_output_test is + + COMPONENT vga_gen + PORT( + clk50 : IN std_logic; + pixel_clock : OUT std_logic; + red_p : OUT std_logic_vector(7 downto 0); + green_p : OUT std_logic_vector(7 downto 0); + blue_p : OUT std_logic_vector(7 downto 0); + blank : OUT std_logic; + hsync : OUT std_logic; + vsync : OUT std_logic + ); + END COMPONENT; + + COMPONENT Minimal_hdmi_symbols + PORT( + clk : IN std_logic; + blank : IN std_logic; + hsync : IN std_logic; + vsync : IN std_logic; + red : IN std_logic; + green : IN std_logic; + blue : IN std_logic; + c0 : OUT std_logic_vector(9 downto 0); + c1 : OUT std_logic_vector(9 downto 0); + c2 : OUT std_logic_vector(9 downto 0) + ); + END COMPONENT; + + COMPONENT serializers + PORT( + clk : IN std_logic; + c0 : IN std_logic_vector(9 downto 0); + c1 : IN std_logic_vector(9 downto 0); + c2 : IN std_logic_vector(9 downto 0); + hdmi_p : OUT std_logic_vector(3 downto 0); + hdmi_n : OUT std_logic_vector(3 downto 0) + ); + END COMPONENT; + + signal pixel_clock : std_logic; + + signal red_p : std_logic_vector(7 downto 0); + signal green_p : std_logic_vector(7 downto 0); + signal blue_p : std_logic_vector(7 downto 0); + signal blank : std_logic; + signal hsync : std_logic; + signal vsync : std_logic; + + signal c0, c1, c2 : std_logic_vector(9 downto 0); +begin + leds <= x"AA"; + +--------------------------------------- +-- Generate a 1280x720 VGA test pattern +--------------------------------------- +Inst_vga_gen: vga_gen PORT MAP( + clk50 => clk50, + pixel_clock => pixel_clock, + red_p => red_p, + green_p => green_p, + blue_p => blue_p, + blank => blank, + hsync => hsync, + vsync => vsync + ); + +--------------------------------------------------- +-- Convert 9 bits of the VGA signals to the DVI-D/TMDS output +--------------------------------------------------- +i_Minimal_hdmi_symbols: Minimal_hdmi_symbols PORT MAP( + clk => pixel_clock, + blank => blank, + hsync => hsync, + vsync => vsync, + red => red_p(7), + green => green_p(7), + blue => blue_p(7), + c0 => c0, + c1 => c1, + c2 => c2 + ); + +i_serializers : serializers PORT MAP ( + clk => pixel_clock, + c0 => c0, + c1 => c1, + c2 => c2, + hdmi_p => hdmi_out_p, + hdmi_n => hdmi_out_n); + +end Behavioral; + diff --git a/test_bench/hdmi_test_generator/minimal_hdmi_symbols.vhd b/test_bench/hdmi_test_generator/minimal_hdmi_symbols.vhd new file mode 100644 index 0000000..ebc48cf --- /dev/null +++ b/test_bench/hdmi_test_generator/minimal_hdmi_symbols.vhd @@ -0,0 +1,282 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field (others => '0')); + signal symbols : STD_LOGIC_VECTOR (29 downto 0) := (others => '0'); + + signal last_blank : std_logic := '0'; + signal last_vsync : std_logic := '0'; + signal last_hsync : std_logic := '0'; + + signal data_island_armed : std_logic := '0'; + signal data_island_index : unsigned(5 downto 0) := (others => '1'); +begin + c0 <= symbols(29 downto 20); + c1 <= symbols(19 downto 10); + c2 <= symbols( 9 downto 0); + +process(clk) + begin + if rising_edge(clk) then + case symbol_queue(0) is + --------------------------------------------------------------- + -- Eight TMDS encoded colours for testing + --------------------------------------------------------------- + when "00000" => symbols <= "0111110000" & "0111110000" & "0111110000"; -- RGB 0x101010 - Black + when "00001" => symbols <= "0111110000" & "0111110000" & "1011110000"; -- RGB 0xEF1010 - Red + when "00010" => symbols <= "0111110000" & "1011110000" & "0111110000"; -- RGB 0x10EF10 - Green + when "00011" => symbols <= "0111110000" & "1011110000" & "1011110000"; -- RGB 0xEFEF10 - Cyan + when "00100" => symbols <= "1011110000" & "0111110000" & "0111110000"; -- RGB 0x1010EF - Blue + when "00101" => symbols <= "1011110000" & "0111110000" & "1011110000"; -- RGB 0xEF10EF - Magenta + when "00110" => symbols <= "1011110000" & "1011110000" & "0111110000"; -- RGB 0x10EFEF - Yellow + when "00111" => symbols <= "1011110000" & "1011110000" & "1011110000"; -- RGB 0xEFEFEF - White + --------------------------------------------------------------- + -- control symbols from 5.4.2 - part of the DVI-D standard + --------------------------------------------------------------- + when "01000" => symbols <= "1101010100" & "1101010100" & "1101010100"; -- CTL periods + when "01001" => symbols <= "0010101011" & "1101010100" & "1101010100"; -- Hsync + when "01010" => symbols <= "0101010100" & "1101010100" & "1101010100"; -- vSync + when "01011" => symbols <= "1010101011" & "1101010100" & "1101010100"; -- vSync+hSync + --------------------------------------------------------------- + -- Symbols to signal the start of a HDMI feature + --------------------------------------------------------------- + when "01100" => symbols <= "0101010100" & "0010101011" & "0010101011"; -- DataIslandPeamble, with VSYNC - 5.2.1.1 + when "01101" => symbols <= "0101100011" & "0100110011" & "0100110011"; -- DataIslandGuardBand, with VSYNC - 5.2.3.3 + when "01110" => symbols <= "1101010100" & "0010101011" & "1101010100"; -- VideoPramble 5.2.1.1 + when "01111" => symbols <= "1011001100" & "0100110011" & "1011001100"; -- VideoGuardBand 5.2.2.1 + + --------------------------------------------------------------- + -- From TERC4 codes in 5.4.3, and data data layout from 5.2.3.1 + -- + -- First nibble is used for the nFirstWordOfPacket (MSB) Header Bit, VSYNC, HSYNC (LSB). + -- The packet is sent where VSYNC = '1' and HSYNC = '0', so we are left with 4 options + -- Second nibble is used for the odd bits the four data sub-packets + -- Third nibble is used for the even bits the four data sub-packets + -- + -- These can be used to contruct a data island with any header + -- and any data in subpacket 0, but all other subpackets + -- must be 0s. + --------------------------------------------------------------- + when "10000" => symbols <= "1011100100" & "1010011100" & "1010011100"; -- 0010 0000 0000, TERC4 coded + when "10001" => symbols <= "1011100100" & "1010011100" & "1001100011"; -- 0010 0000 0001, TERC4 coded + when "10010" => symbols <= "1011100100" & "1001100011" & "1010011100"; -- 0010 0000 0000, TERC4 coded + when "10011" => symbols <= "1011100100" & "1001100011" & "1001100011"; -- 0010 0001 0001, TERC4 coded + when "10100" => symbols <= "0110001110" & "1010011100" & "1010011100"; -- 0110 0000 0000, TERC4 coded + when "10101" => symbols <= "0110001110" & "1010011100" & "1001100011"; -- 0110 0000 0001, TERC4 coded + when "10110" => symbols <= "0110001110" & "1001100011" & "1010011100"; -- 0110 0001 0000, TERC4 coded + when "10111" => symbols <= "0110001110" & "1001100011" & "1001100011"; -- 0110 0001 0001, TERC4 coded + when "11000" => symbols <= "0110011100" & "1010011100" & "1010011100"; -- 1010 0000 0000, TERC4 coded + when "11001" => symbols <= "0110011100" & "1010011100" & "1001100011"; -- 1010 0000 0001, TERC4 coded + when "11010" => symbols <= "0110011100" & "1001100011" & "1010011100"; -- 1010 0001 0000, TERC4 coded + when "11011" => symbols <= "0110011100" & "1001100011" & "1001100011"; -- 1010 0001 0001, TERC4 coded + when "11100" => symbols <= "0101100011" & "1010011100" & "1010011100"; -- 1110 0000 0000, TERC4 coded + when "11101" => symbols <= "0101100011" & "1010011100" & "1001100011"; -- 1110 0000 0001, TERC4 coded + when "11110" => symbols <= "0101100011" & "1001100011" & "1010011100"; -- 1110 0001 0000, TERC4 coded + when "11111" => symbols <= "0101100011" & "1001100011" & "1001100011"; -- 1110 0001 0001, TERC4 coded + + when others => symbols <= (others => '0'); + end case; + + if blank = '0' then + -- Are we being asked to send video data? If so we need to send a peramble + if last_blank = '1' then + symbol_queue(10) <= "00" & blue & green & red; + symbol_queue(9) <= "01111"; -- Video Guard Band + symbol_queue(8) <= "01111"; + symbol_queue(7) <= "01110"; -- Video Preamble + symbol_queue(6) <= "01110"; + symbol_queue(5) <= "01110"; + symbol_queue(4) <= "01110"; + symbol_queue(3) <= "01110"; + symbol_queue(2) <= "01110"; + symbol_queue(1) <= "01110"; + symbol_queue(0) <= "01110"; + else + symbol_queue(0 to 9) <= symbol_queue(1 to 10); + symbol_queue(10) <= "00" & blue & green & red; + end if; + else + -- Just merge in the syncs into the control period + case data_island_index is + when "000000" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000001" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000010" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000011" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000100" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000101" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000110" => symbol_queue(10) <= "01100"; -- Data island preamble + when "000111" => symbol_queue(10) <= "01100"; -- Data island preamble + when "001000" => symbol_queue(10) <= "01101"; -- Data island Guard Band + when "001001" => symbol_queue(10) <= "01101"; -- Data island Guard Band + + + ------------------------- + -- For a YCC mode AVI Infoframe Data Island + ------------------------- + -- Data Island (0-7) + when "001010" => symbol_queue(10) <= "10011"; -- First word + when "001011" => symbol_queue(10) <= "11111"; + when "001100" => symbol_queue(10) <= "11001"; + when "001101" => symbol_queue(10) <= "11000"; + when "001110" => symbol_queue(10) <= "11000"; + when "001111" => symbol_queue(10) <= "11000"; + when "010000" => symbol_queue(10) <= "11000"; + when "010001" => symbol_queue(10) <= "11110"; + -- Data Island (8-15) + when "010010" => symbol_queue(10) <= "11000"; + when "010011" => symbol_queue(10) <= "11100"; + when "010100" => symbol_queue(10) <= "11000"; + when "010101" => symbol_queue(10) <= "11000"; + when "010110" => symbol_queue(10) <= "11000"; + when "010111" => symbol_queue(10) <= "11000"; + when "011000" => symbol_queue(10) <= "11000"; + when "011001" => symbol_queue(10) <= "11000"; + -- Data Island (16-23) + when "011010" => symbol_queue(10) <= "11100"; + when "011011" => symbol_queue(10) <= "11000"; + when "011100" => symbol_queue(10) <= "11100"; + when "011101" => symbol_queue(10) <= "11100"; + when "011110" => symbol_queue(10) <= "11000"; + when "011111" => symbol_queue(10) <= "11000"; + when "100000" => symbol_queue(10) <= "11000"; + when "100001" => symbol_queue(10) <= "11000"; + -- Data Island (24-31) + when "100010" => symbol_queue(10) <= "11000"; + when "100011" => symbol_queue(10) <= "11000"; + when "100100" => symbol_queue(10) <= "11100"; + when "100101" => symbol_queue(10) <= "11000"; + when "100110" => symbol_queue(10) <= "11010"; + when "100111" => symbol_queue(10) <= "11100"; + when "101000" => symbol_queue(10) <= "11111"; + when "101001" => symbol_queue(10) <= "11110"; + + ------------------------- + -- For a NULL Data Island + ------------------------- + -- Data Island (0-7) +-- when "001010" => symbol_queue(10) <= "10000"; -- First word +-- when "001011" => symbol_queue(10) <= "11000"; +-- when "001100" => symbol_queue(10) <= "11000"; +-- when "001101" => symbol_queue(10) <= "11000"; +-- when "001110" => symbol_queue(10) <= "11000"; +-- when "001111" => symbol_queue(10) <= "11000"; +-- when "010000" => symbol_queue(10) <= "11000"; +-- when "010001" => symbol_queue(10) <= "11000"; + -- Data Island (8-15) +-- when "010010" => symbol_queue(10) <= "11000"; +-- when "010011" => symbol_queue(10) <= "11000"; +-- when "010100" => symbol_queue(10) <= "11000"; +-- when "010101" => symbol_queue(10) <= "11000"; +-- when "010110" => symbol_queue(10) <= "11000"; +-- when "010111" => symbol_queue(10) <= "11000"; +-- when "011000" => symbol_queue(10) <= "11000"; +-- when "011001" => symbol_queue(10) <= "11000"; + -- Data Island (16-23) +-- when "011010" => symbol_queue(10) <= "11000"; +-- when "011011" => symbol_queue(10) <= "11000"; +-- when "011100" => symbol_queue(10) <= "11000"; +-- when "011101" => symbol_queue(10) <= "11000"; +-- when "011110" => symbol_queue(10) <= "11000"; +-- when "011111" => symbol_queue(10) <= "11000"; +-- when "100000" => symbol_queue(10) <= "11000"; +-- when "100001" => symbol_queue(10) <= "11000"; + -- Data Island (24-31) +-- when "100010" => symbol_queue(10) <= "11000"; +-- when "100011" => symbol_queue(10) <= "11000"; +-- when "100100" => symbol_queue(10) <= "11000"; +-- when "100101" => symbol_queue(10) <= "11000"; +-- when "100110" => symbol_queue(10) <= "11000"; +-- when "100111" => symbol_queue(10) <= "11000"; +-- when "101000" => symbol_queue(10) <= "11000"; +-- when "101001" => symbol_queue(10) <= "11000"; + + -- Trailing guard band + when "101010" => symbol_queue(10) <= "01101"; -- Data island Guard Band + when "101011" => symbol_queue(10) <= "01101"; -- Data island Guard Band + -- There has to be four CTL symbols before the next block of video our data, + -- But that won't be a problem for us, we will have the rest of the vertical + -- Blanking interval + when others => symbol_queue(10) <= "010" & Vsync & Hsync; + end case; + + symbol_queue(0 to 9) <= symbol_queue(1 to 10); + end if; + + if data_island_index /= "111111" then + data_island_index <= data_island_index + 1; + end if; + + -- If we see the rising edge of vsync we need to send + -- a data island the next time we see the hsync signal + -- drop. + if last_vsync = '0' and vsync = '1' then + data_island_armed <= '1'; + end if; + + if data_island_armed = '1' and last_hsync = '1' and hsync = '0' then + data_island_index <= (others => '0'); + data_island_armed <= '0'; + end if; + + last_blank <= blank; + last_hsync <= hsync; + last_vsync <= vsync; + end if; + end process; + +end Behavioral; + diff --git a/test_bench/hdmi_test_generator/serializers.vhd b/test_bench/hdmi_test_generator/serializers.vhd new file mode 100644 index 0000000..d9f55d3 --- /dev/null +++ b/test_bench/hdmi_test_generator/serializers.vhd @@ -0,0 +1,165 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Convert 3x 10-bit symbols to three serial channels and the clock channel. +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +library UNISIM; +use UNISIM.VComponents.all; + +entity serializers is + Port ( clk : in STD_LOGIC; + c0 : in STD_LOGIC_VECTOR (9 downto 0); + c1 : in STD_LOGIC_VECTOR (9 downto 0); + c2 : in STD_LOGIC_VECTOR (9 downto 0); + hdmi_p : out STD_LOGIC_VECTOR (3 downto 0); + hdmi_n : out STD_LOGIC_VECTOR (3 downto 0)); +end serializers; + +architecture Behavioral of serializers is + -- For holding the outward bound TMDS symbols in the slow and fast domain + signal c0_high_speed : std_logic_vector(9 downto 0) := (others => '0'); + signal c1_high_speed : std_logic_vector(9 downto 0) := (others => '0'); + signal c2_high_speed : std_logic_vector(9 downto 0) := (others => '0'); + signal clk_high_speed : std_logic_vector(9 downto 0) := (others => '0'); + signal c2_output_bits : std_logic_vector(1 downto 0) := "00"; + signal c1_output_bits : std_logic_vector(1 downto 0) := "00"; + signal c0_output_bits : std_logic_vector(1 downto 0) := "00"; + signal clk_output_bits : std_logic_vector(1 downto 0) := "00"; + + -- Controlling the transfers into the high speed domain + signal latch_high_speed : std_logic_vector(4 downto 0) := "00001"; + + -- From the DDR outputs to the output buffers + signal c0_serial, c1_serial, c2_serial, clk_serial : std_logic; + + -- For generating the x5 clocks + signal clk_x5, clk_x5_n, clk_x5_unbuffered : std_logic; + signal clk_feedback : std_logic; + + -- To glue the HSYNC and VSYNC into the control character. + signal syncs : std_logic_vector(1 downto 0); +begin + +process(clk_x5) + begin + --------------------------------------------------------------- + -- Now take the 10-bit words and take it into the high-speed + -- clock domain once every five cycles. + -- + -- Then send out two bits every clock cycle using DDR output + -- registers. + --------------------------------------------------------------- + if rising_edge(clk_x5) then + c0_output_bits <= c0_high_speed(1 downto 0); + c1_output_bits <= c1_high_speed(1 downto 0); + c2_output_bits <= c2_high_speed(1 downto 0); + clk_output_bits <= clk_high_speed(1 downto 0); + + if latch_high_speed(0) = '1' then + c0_high_speed <= c0; + c1_high_speed <= c1; + c2_high_speed <= c2; + clk_high_speed <= "0000011111"; + else + c0_high_speed <= "00" & c0_high_speed(9 downto 2); + c1_high_speed <= "00" & c1_high_speed(9 downto 2); + c2_high_speed <= "00" & c2_high_speed(9 downto 2); + clk_high_speed <= "00" & clk_high_speed(9 downto 2); + end if; + latch_high_speed <= latch_high_speed(0) & latch_high_speed(4 downto 1); + end if; + end process; + + ------------------------------------------------------------------ + -- Convert the TMDS codes into a serial stream, two bits at a time + ------------------------------------------------------------------ + clk_x5_n <= not clk_x5; +c0_to_serial: ODDR2 + generic map(DDR_ALIGNMENT => "C0", INIT => '0', SRTYPE => "ASYNC") + port map (C0 => clk_x5, C1 => clk_x5_n, CE => '1', R => '0', S => '0', + D0 => C0_output_bits(0), D1 => C0_output_bits(1), Q => c0_serial); +OBUFDS_c0 : OBUFDS port map ( O => hdmi_p(0), OB => hdmi_n(0), I => c0_serial); + +c1_to_serial: ODDR2 + generic map(DDR_ALIGNMENT => "C0", INIT => '0', SRTYPE => "ASYNC") + port map (C0 => clk_x5, C1 => clk_x5_n, CE => '1', R => '0', S => '0', + D0 => C1_output_bits(0), D1 => C1_output_bits(1), Q => c1_serial); +OBUFDS_c1 : OBUFDS port map ( O => hdmi_p(1), OB => hdmi_n(1), I => c1_serial); + +c2_to_serial: ODDR2 + generic map(DDR_ALIGNMENT => "C0", INIT => '0', SRTYPE => "ASYNC") + port map (C0 => clk_x5, C1 => clk_x5_n, CE => '1', R => '0', S => '0', + D0 => C2_output_bits(0), D1 => C2_output_bits(1), Q => c2_serial); +OBUFDS_c2 : OBUFDS port map ( O => hdmi_p(2), OB => hdmi_n(2), I => c2_serial); + +clk_to_serial: ODDR2 + generic map(DDR_ALIGNMENT => "C0", INIT => '0', SRTYPE => "ASYNC") + port map (C0 => clk_x5, C1 => clk_x5_n, CE => '1', R => '0', S => '0', + D0 => Clk_output_bits(0), D1 => Clk_output_bits(1), Q => clk_serial); +OBUFDS_clk : OBUFDS port map ( O => hdmi_p(3), OB => hdmi_n(3), I => clk_serial); + + ------------------------------------------------------------------ + -- Use a PLL to generate a x5 clock, which is used to drive + -- the DDR registers.This allows 10 bits to be sent for every + -- pixel clock + ------------------------------------------------------------------ +PLL_BASE_inst : PLL_BASE + generic map ( + CLKFBOUT_MULT => 10, + CLKOUT0_DIVIDE => 2, CLKOUT0_PHASE => 0.0, -- Output 5x original frequency + CLK_FEEDBACK => "CLKFBOUT", + CLKIN_PERIOD => 13.33, + DIVCLK_DIVIDE => 1 + ) + port map ( + CLKFBOUT => clk_feedback, + CLKOUT0 => clk_x5_unbuffered, + CLKFBIN => clk_feedback, + CLKIN => clk, + RST => '0' + ); + +BUFG_pclkx5 : BUFG port map ( I => clk_x5_unbuffered, O => clk_x5); + +end Behavioral; + diff --git a/test_bench/hdmi_test_generator/vga_clocking.vhd b/test_bench/hdmi_test_generator/vga_clocking.vhd new file mode 100644 index 0000000..cb278bd --- /dev/null +++ b/test_bench/hdmi_test_generator/vga_clocking.vhd @@ -0,0 +1,89 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field 16, + CLKOUT0_DIVIDE => 20, CLKOUT0_PHASE => 0.0, -- Output pixel clock, 1.5x original frequency + CLK_FEEDBACK => "CLKFBOUT", -- Clock source to drive CLKFBIN ("CLKFBOUT" or "CLKOUT0") + CLKIN_PERIOD => 20.0, -- IMPORTANT! 20.00 => 50MHz + DIVCLK_DIVIDE => 1 -- Division value for all output clocks (1-52) + ) + port map ( + CLKFBOUT => clk_feedback, + CLKOUT0 => clock_x1_unbuffered, + CLKOUT1 => open, + CLKOUT2 => open, + CLKOUT3 => open, + CLKOUT4 => open, + CLKOUT5 => open, + LOCKED => pll_locked, + CLKFBIN => clk_feedback, + CLKIN => clk50_buffered, + RST => '0' -- 1-bit input: Reset input + ); + +BUFG_clk : BUFG port map ( I => clk50, O => clk50_buffered); +BUFG_pclock : BUFG port map ( I => clock_x1_unbuffered, O => clock_x1); + +end Behavioral; \ No newline at end of file diff --git a/test_bench/hdmi_test_generator/vga_gen.vhd b/test_bench/hdmi_test_generator/vga_gen.vhd new file mode 100644 index 0000000..d67e507 --- /dev/null +++ b/test_bench/hdmi_test_generator/vga_gen.vhd @@ -0,0 +1,142 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Description: Generates a test 1280x720 signal +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +----------------------------------------------------------------------------------library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity vga_gen is + Port ( clk50 : in STD_LOGIC; + pixel_clock : out std_logic; + + red_p : out STD_LOGIC_VECTOR (7 downto 0) := (others => '0'); + green_p : out STD_LOGIC_VECTOR (7 downto 0) := (others => '0'); + blue_p : out STD_LOGIC_VECTOR (7 downto 0) := (others => '0'); + blank : out STD_LOGIC := '0'; + hsync : out STD_LOGIC := '0'; + vsync : out STD_LOGIC := '0'); +end vga_gen; + +architecture Behavioral of vga_gen is + COMPONENT vga_clocking + PORT( clk50 : IN std_logic; + pixel_clock : OUT std_logic); + END COMPONENT; + + constant h_rez : natural := 800; + constant h_sync_start : natural := 800+40; + constant h_sync_end : natural := 800+40+128; + constant h_max : natural := 1056; + signal h_count : unsigned(11 downto 0) := (others => '0'); + signal h_offset : unsigned(7 downto 0) := (others => '0'); + + constant v_rez : natural := 600; + constant v_sync_start : natural := 600+1; + constant v_sync_end : natural := 600+1+4; + constant v_max : natural := 628; + signal v_count : unsigned(11 downto 0) := x"250"; + signal v_offset : unsigned(7 downto 0) := (others => '0'); + signal clk40 : std_logic; +begin + +Inst_clocking: vga_clocking PORT MAP( + clk50 => clk50, + pixel_clock => clk40 + ); + pixel_clock <= clk40; + + +process(clk40) + begin + if rising_edge(clk40) then + if h_count < h_rez and v_count < v_rez then + red_p <= std_logic_vector(h_count(7 downto 0)+h_offset); + green_p <= std_logic_vector(v_count(7 downto 0)+v_offset); + blue_p <= std_logic_vector(h_count(7 downto 0)+v_count(7 downto 0)); + blank <= '0'; + if h_count = 0 or h_count = h_rez-1 then + red_p <= (others => '1'); + green_p <= (others => '1'); + blue_p <= (others => '1'); + end if; + if v_count = 0 or v_count = v_rez-1 then + red_p <= (others => '1'); + green_p <= (others => '0'); + blue_p <= (others => '0'); + end if; + else + red_p <= (others => '0'); + green_p <= (others => '0'); + blue_p <= (others => '0'); + blank <= '1'; + end if; + + if h_count >= h_sync_start and h_count < h_sync_end then + hsync <= '1'; + else + hsync <= '0'; + end if; + + if v_count >= v_sync_start and v_count < v_sync_end then + vsync <= '1'; + else + vsync <= '0'; + end if; + + if h_count = h_max then + h_count <= (others => '0'); + if v_count = v_max then + h_offset <= h_offset + 1; + v_offset <= v_offset + 1; + v_count <= (others => '0'); + else + v_count <= v_count+1; + end if; + else + h_count <= h_count+1; + end if; + + end if; + end process; + +end Behavioral; + diff --git a/test_bench/tb_audio_to_db.vhd b/test_bench/tb_audio_to_db.vhd new file mode 100644 index 0000000..e4eb4ca --- /dev/null +++ b/test_bench/tb_audio_to_db.vhd @@ -0,0 +1,110 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: tb_audio_to_db - Behavioral +-- +-- Description: A testbench for the audio sample to db level calculation +-- +---------------------------------------------------------------------------------- +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +---------------------------------------------------------------------------------- +----- Want to say thanks? -------------------------------------------------------- +---------------------------------------------------------------------------------- +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +------------------------------------------------------------------------------------ + + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; +use IEEE.NUMERIC_STD.ALL; + +entity tb_audio_to_db is +end tb_audio_to_db; + +architecture Behavioral of tb_audio_to_db is + component audio_to_db is + Port ( clk : in STD_LOGIC; + + in_channel : in STD_LOGIC_VECTOR (2 downto 0); + in_de : in STD_LOGIC; + in_sample : in STD_LOGIC_VECTOR (23 downto 0); + + out_channel : out STD_LOGIC_VECTOR (2 downto 0); + out_de : out STD_LOGIC; + out_level : out STD_LOGIC_VECTOR (5 downto 0)); + end component; + + signal clk : STD_LOGIC := '0'; + + signal in_channel : STD_LOGIC_VECTOR (2 downto 0) := (others => '0'); + signal in_de : STD_LOGIC := '1'; + signal in_sample : STD_LOGIC_VECTOR (23 downto 0) := (others => '0'); + + signal out_channel : STD_LOGIC_VECTOR (2 downto 0); + signal out_de : STD_LOGIC; + signal out_level : STD_LOGIC_VECTOR (5 downto 0); + +begin + +process + begin + wait for 5 ns; + clk <= '1'; + wait for 5 ns; + clk <= '0'; + end process; + +process + begin + wait until rising_edge(clk); + in_de <= '1'; + in_sample <= in_sample(in_sample'high-1 downto 0) & not in_sample(in_sample'high); + wait until rising_edge(clk); + in_de <= '0'; + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + wait until rising_edge(clk); + end process; + +uut: audio_to_db port map ( + clk => clk, + in_channel => in_channel, + in_de => in_de, + in_sample => in_sample, + + out_channel => out_channel, + out_de => out_de, + out_level => out_level); + +end Behavioral; diff --git a/test_bench/tb_convert_yCbCr_to_RGB.vhd b/test_bench/tb_convert_yCbCr_to_RGB.vhd new file mode 100644 index 0000000..974c6b9 --- /dev/null +++ b/test_bench/tb_convert_yCbCr_to_RGB.vhd @@ -0,0 +1,147 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Description: A testbench for YCbCr to RGB decoding +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +------------------------------------------------------------------------------------ +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +-- +---------------------------------------------------------------------------------- + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +-- Uncomment the following library declaration if using +-- arithmetic functions with Signed or Unsigned values +--use IEEE.NUMERIC_STD.ALL; + +-- Uncomment the following library declaration if instantiating +-- any Xilinx leaf cells in this code. +--library UNISIM; +--use UNISIM.VComponents.all; + +entity tb_convert_yCbCr_to_RGB is +end tb_convert_yCbCr_to_RGB; + +architecture Behavioral of tb_convert_yCbCr_to_RGB is + component conversion_YCbCr_to_RGB is + port ( clk : in std_Logic; + input_is_YCbCr : in std_Logic; + + ------------------------ + in_blank : in std_logic; + in_hsync : in std_logic; + in_vsync : in std_logic; + in_U : in std_logic_vector(11 downto 0); -- B or Cb + in_V : in std_logic_vector(11 downto 0); -- G or Y + in_W : in std_logic_vector(11 downto 0); -- R or Cr + + ------------------------ + out_blank : out std_logic; + out_hsync : out std_logic; + out_vsync : out std_logic; + out_R : out std_logic_vector(11 downto 0); + out_G : out std_logic_vector(11 downto 0); + out_B : out std_logic_vector(11 downto 0)); + end component; + + signal clk : std_Logic := '0'; + signal input_is_YCbCr : std_Logic := '0'; + signal in_blank : std_logic := '0'; + signal in_hsync : std_logic := '0'; + signal in_vsync : std_logic := '0'; + signal in_U : std_logic_vector(11 downto 0) := x"800"; -- B or Cb + signal in_V : std_logic_vector(11 downto 0) := x"800"; -- G or Y + signal in_W : std_logic_vector(11 downto 0) := x"800"; -- R or Cr + signal out_blank : std_logic := '0'; + signal out_hsync : std_logic := '0'; + signal out_vsync : std_logic := '0'; + signal out_R : std_logic_vector(11 downto 0); + signal out_G : std_logic_vector(11 downto 0); + signal out_B : std_logic_vector(11 downto 0); + +begin + +process + begin + wait for 5 ns; + clk <= not clk; + end process; + +stim: process + begin + wait for 100 ns; + in_U <= x"100"; + wait for 100 ns; + in_U <= x"EFF"; + wait for 100 ns; + in_U <= x"800"; + + wait for 100 ns; + in_V <= x"100"; + wait for 100 ns; + in_V <= x"EFF"; + wait for 100 ns; + in_V <= x"800"; + + wait for 100 ns; + in_W <= x"100"; + wait for 100 ns; + in_W <= x"EFF"; + wait for 100 ns; + in_W <= x"800"; + + end process; +uut: conversion_YCbCr_to_RGB port map ( + clk => clk, + input_is_YCbCr => '1', + ------------------------ + in_blank => in_blank, + in_hsync => in_hsync, + in_vsync => in_vsync, + in_U => in_U, + in_V => in_V, + in_W => in_W, + ------------------------ + out_blank => out_blank, + out_hsync => out_hsync, + out_vsync => out_vsync, + out_R => out_R, + out_G => out_G, + out_B => out_B); + +end Behavioral; diff --git a/test_bench/tb_hdmi_decode.vhd b/test_bench/tb_hdmi_decode.vhd new file mode 100644 index 0000000..39e77c6 --- /dev/null +++ b/test_bench/tb_hdmi_decode.vhd @@ -0,0 +1,308 @@ +---------------------------------------------------------------------------------- +-- Engineer: Mike Field +-- +-- Module Name: tb_hdmi_decode - Behavioral +-- +-- Description: A testbench for testing HDMI decoding +-- +------------------------------------------------------------------------------------ +-- The MIT License (MIT) +-- +-- Copyright (c) 2015 Michael Alan Field +-- +-- Permission is hereby granted, free of charge, to any person obtaining a copy +-- of this software and associated documentation files (the "Software"), to deal +-- in the Software without restriction, including without limitation the rights +-- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +-- copies of the Software, and to permit persons to whom the Software is +-- furnished to do so, subject to the following conditions: +-- +-- The above copyright notice and this permission notice shall be included in +-- all copies or substantial portions of the Software. +-- +-- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +-- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +-- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +-- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +-- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +-- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +-- THE SOFTWARE. +---------------------------------------------------------------------------------- +----- Want to say thanks? ---------------------------------------------------------- +------------------------------------------------------------------------------------ +-- +-- This design has taken many hours - with the industry metric of 30 lines +-- per day, it is equivalent to about 6 months of work. I'm more than happy +-- to share it if you can make use of it. It is released under the MIT license, +-- so you are not under any onus to say thanks, but.... +-- +-- If you what to say thanks for this design how about trying PayPal? +-- Educational use - Enough for a beer +-- Hobbyist use - Enough for a pizza +-- Research use - Enough to take the family out to dinner +-- Commercial use - A weeks pay for an engineer (I wish!) +------------------------------------------------------------------------------------ + +library IEEE; +use IEEE.STD_LOGIC_1164.ALL; + +entity tb_hdmi_decode is +end tb_hdmi_decode; + +architecture Behavioral of tb_hdmi_decode is + component hdmi_design is + Port ( + clk100 : in STD_LOGIC; + -- Control signals + led : out std_logic_vector(7 downto 0); + sw : in std_logic_vector(2 downto 0) :=(others => '0'); + debug_pmod : out std_logic_vector(7 downto 0) :=(others => '0'); + + --HDMI input signals + hdmi_rx_cec : inout std_logic; + hdmi_rx_hpa : out std_logic; + hdmi_rx_scl : in std_logic; + hdmi_rx_sda : inout std_logic; + hdmi_rx_txen : out std_logic; + hdmi_rx_clk_n : in std_logic; + hdmi_rx_clk_p : in std_logic; + hdmi_rx_n : in std_logic_vector(2 downto 0); + hdmi_rx_p : in std_logic_vector(2 downto 0); + + --- HDMI out + hdmi_tx_cec : inout std_logic; + hdmi_tx_clk_n : out std_logic; + hdmi_tx_clk_p : out std_logic; + hdmi_tx_hpd : in std_logic; + hdmi_tx_rscl : inout std_logic; + hdmi_tx_rsda : inout std_logic; + hdmi_tx_p : out std_logic_vector(2 downto 0); + hdmi_tx_n : out std_logic_vector(2 downto 0) + ); + end component; + + component hdmi_output_test is + Port ( clk50 : in STD_LOGIC; + + hdmi_out_p : out STD_LOGIC_VECTOR(3 downto 0); + hdmi_out_n : out STD_LOGIC_VECTOR(3 downto 0); + + leds : out std_logic_vector(7 downto 0)); + end component; + + + signal clk : std_logic := '0'; + signal clk50 : std_logic := '1'; + signal led : std_logic_vector(7 downto 0); + signal hdmi_rx_cec : std_logic; + signal hdmi_rx_hpa : std_logic; + signal hdmi_rx_scl : std_logic; + signal hdmi_rx_sda : std_logic; + signal hdmi_rx_txen : std_logic; + signal hdmi_rx_clk_n : std_logic; + signal hdmi_rx_clk_p : std_logic; + signal hdmi2_rx_clk_n : std_logic := '1'; + signal hdmi2_rx_clk_p : std_logic := '0'; + signal hdmi_out_n : std_logic_vector(3 downto 0); + signal hdmi_out_p : std_logic_vector(3 downto 0); + signal hdmi_rx_n : std_logic_vector(2 downto 0); + signal hdmi_rx_p : std_logic_vector(2 downto 0); + signal hdmi_tx_cec : std_logic; + signal hdmi_tx_clk_n : std_logic; + signal hdmi_tx_clk_p : std_logic; + signal hdmi_tx_hpd : std_logic; + signal hdmi_tx_rscl : std_logic; + signal hdmi_tx_rsda : std_logic; + signal hdmi_tx_p : std_logic_vector(2 downto 0); + signal hdmi_tx_n : std_logic_vector(2 downto 0); + + signal sdat_drive : std_logic := '1'; +begin +hdmi_rx_sda <= '0' when sdat_drive = '0' else 'H'; + + hdmi_rx_p <= transport hdmi_out_p(2 downto 0) after 5.00 ns; + hdmi_rx_n <= transport hdmi_out_n(2 downto 0) after 5.00 ns; + hdmi_rx_clk_p <= transport hdmi_out_p(3) after 1.25 ns; + hdmi_rx_clk_n <= transport hdmi_out_n(3) after 1.25 ns; + +clk_proc: process +begin + wait for 7.0 ns; + while 1 = 1 loop + wait for 5.0 ns; + clk <= not clk; + end loop; +end process; + +clk50_proc: process +begin + wait for 7.0 ns; + while 1 = 1 loop + wait for 5.0 ns; + clk50 <= not clk50; + end loop; +end process; + +i_gen_signal: hdmi_output_test port map ( + clk50 => clk50, + hdmi_out_p => hdmi_out_p, + hdmi_out_n => hdmi_out_n, + leds => open); + +uut: hdmi_design Port map ( + clk100 => clk, + led => open, + sw => (others => '0'), + debug_pmod => open, + --HDMI in + hdmi_rx_cec => hdmi_rx_cec, + hdmi_rx_hpa => hdmi_rx_hpa, + hdmi_rx_scl => hdmi_rx_scl, + hdmi_rx_sda => hdmi_rx_sda, + hdmi_rx_txen => hdmi_rx_txen, + hdmi_rx_clk_n => hdmi_rx_clk_n, + hdmi_rx_clk_p => hdmi_rx_clk_p, + hdmi_rx_n => hdmi_rx_n, + hdmi_rx_p => hdmi_rx_p, + + --- HDMI out + hdmi_tx_cec => hdmi_tx_cec, + hdmi_tx_clk_n => hdmi_tx_clk_n, + hdmi_tx_clk_p => hdmi_tx_clk_p, + hdmi_tx_hpd => hdmi_tx_hpd, + hdmi_tx_rscl => hdmi_tx_rscl, + hdmi_tx_rsda => hdmi_tx_rsda, + hdmi_tx_p => hdmi_tx_p, + hdmi_tx_n => hdmi_tx_n +); + +edid_test_proc: process +begin + hdmi_rx_scl <= '1'; + wait for 1 us; +-- START condition + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- DEVICE ADDRESS FOR WRITE +-- dev bit 7 + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 6 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 6 + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 4 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 3 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 2 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 1 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 0 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- Slave ACK +-- Device to ack + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- SEND WRITE ADDRESS +-- addr bit 7 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 6 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 6 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 4 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 3 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 2 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 1 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- addr bit 0 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- Slave ACK + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- repeated START condition + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; + wait for 400 ns; sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- DEVICE ADDRESS / READ - +-- dev bit 7 + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 6 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 6 + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 4 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 3 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 2 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 1 + sdat_drive <= '0'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- dev bit 0 - READ! + sdat_drive <= '1'; wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + +-- ACK???? +-- Device to ack + sdat_drive <= '1'; + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + +for i in 1 to 127 loop +-- READ First byte +-- read bit 7 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 6 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 6 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 4 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 3 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 2 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 1 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 0 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + sdat_drive <= '1'; + +-- Host to ack + sdat_drive <= '0'; + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + sdat_drive <= '1'; +end loop; +-- READ Second +-- read bit 7 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 6 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 6 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 4 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 3 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 2 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 1 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; +-- read bit 0 + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + sdat_drive <= '1'; + +-- Master NACK + sdat_drive <= '1'; + wait for 200 ns; hdmi_rx_scl <= '1'; wait for 400 ns; hdmi_rx_scl <= '0'; wait for 200 ns; + sdat_drive <= '1'; + +-- STOP + sdat_drive <= '1'; + wait for 200 ns; + hdmi_rx_scl <= '1'; + wait for 200 ns; + + wait; +end process; + +end Behavioral;