├── .gitignore ├── LICENSE ├── impl ├── enclustra_artix7.xdc └── arty-z7.xdc ├── rtl ├── clock_gen.vhd ├── pattern_generator.vhd ├── rgb2tmds.vhd ├── tmds_encoder.vhd ├── objectbuffer.vhd ├── hdmi_out.vhd ├── timing_generator.vhd └── serializer.vhd ├── makefile ├── sim └── tb_hdmi_out.vhd └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | debug 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2017 Furkan Cayci 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy of 4 | this software and associated documentation files (the "Software"), to deal in 5 | the Software without restriction, including without limitation the rights to 6 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 7 | of the Software, and to permit persons to whom the Software is furnished to do 8 | so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /impl/enclustra_artix7.xdc: -------------------------------------------------------------------------------- 1 | ## HDMI out constraints file. Can be used in Enclustra artix7 kit 2 | 3 | ## Clock signal 50 MHz 4 | set_property -dict { PACKAGE_PIN P17 IOSTANDARD LVCMOS33 } [get_ports { clk }]; 5 | create_clock -add -name sys_clk_pin -period 20.00 -waveform {0 4} [get_ports { clk }]; 6 | 7 | ##Buttons 8 | set_property -dict { PACKAGE_PIN F1 IOSTANDARD LVCMOS33 } [get_ports { rst }]; 9 | 10 | ##HDMI Tx 11 | 12 | set_property -dict { PACKAGE_PIN A4 IOSTANDARD TMDS_33 } [get_ports { data_p[2] }]; 13 | set_property -dict { PACKAGE_PIN A3 IOSTANDARD TMDS_33 } [get_ports { data_n[2] }]; 14 | set_property -dict { PACKAGE_PIN B7 IOSTANDARD TMDS_33 } [get_ports { data_p[1] }]; 15 | set_property -dict { PACKAGE_PIN B6 IOSTANDARD TMDS_33 } [get_ports { data_n[1] }]; 16 | set_property -dict { PACKAGE_PIN A6 IOSTANDARD TMDS_33 } [get_ports { data_p[0] }]; 17 | set_property -dict { PACKAGE_PIN A5 IOSTANDARD TMDS_33 } [get_ports { data_n[0] }]; 18 | set_property -dict { PACKAGE_PIN D5 IOSTANDARD TMDS_33 } [get_ports { clk_p }]; 19 | set_property -dict { PACKAGE_PIN D4 IOSTANDARD TMDS_33 } [get_ports { clk_n }]; 20 | #set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_hpdn }]; 21 | #set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_cec }]; 22 | -------------------------------------------------------------------------------- /rtl/clock_gen.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: generate 1x pixel and 5x serial clocks from the input clock 3 | -- default to 125 Mhz input clock 4 | -- 25 and 125 Mhz output clocks 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | library unisim; 9 | use unisim.vcomponents.all; 10 | 11 | entity clock_gen is 12 | generic ( 13 | CLKIN_PERIOD : real := 8.000; -- input clock period (8ns) 14 | CLK_MULTIPLY : integer := 8; -- multiplier 15 | CLK_DIVIDE : integer := 1; -- divider 16 | CLKOUT0_DIV : integer := 8; -- serial clock divider 17 | CLKOUT1_DIV : integer := 40 -- pixel clock divider 18 | ); 19 | port( 20 | clk_i : in std_logic; -- input clock 21 | clk0_o : out std_logic; -- serial clock 22 | clk1_o : out std_logic -- pixel clock 23 | ); 24 | end clock_gen; 25 | 26 | architecture rtl of clock_gen is 27 | 28 | signal pllclk0, pllclk1 : std_logic; 29 | signal clkfbout : std_logic; 30 | 31 | begin 32 | 33 | -- buffer output clocks 34 | clk0buf: BUFG port map (I=>pllclk0, O=>clk0_o); 35 | clk1buf: BUFG port map (I=>pllclk1, O=>clk1_o); 36 | 37 | clock: PLLE2_BASE generic map ( 38 | clkin1_period => CLKIN_PERIOD, 39 | clkfbout_mult => CLK_MULTIPLY, 40 | clkout0_divide => CLKOUT0_DIV, 41 | clkout1_divide => CLKOUT1_DIV, 42 | divclk_divide => CLK_DIVIDE 43 | ) 44 | port map( 45 | rst => '0', 46 | pwrdwn => '0', 47 | clkin1 => clk_i, 48 | clkfbin => clkfbout, 49 | clkfbout => clkfbout, 50 | clkout0 => pllclk0, 51 | clkout1 => pllclk1 52 | ); 53 | 54 | end rtl; 55 | -------------------------------------------------------------------------------- /impl/arty-z7.xdc: -------------------------------------------------------------------------------- 1 | ## HDMI out constraints file. Can be used in Arty-Z7-20, Pynq-Z1, and Pynq-Z2 since they share the same pinout 2 | 3 | ## Clock signal 125 MHz 4 | set_property -dict { PACKAGE_PIN H16 IOSTANDARD LVCMOS33 } [get_ports { clk }]; #IO_L13P_T2_MRCC_35 Sch=sysclk 5 | create_clock -add -name sys_clk_pin -period 8.00 -waveform {0 4} [get_ports { clk }]; 6 | 7 | ##Buttons 8 | set_property -dict { PACKAGE_PIN D19 IOSTANDARD LVCMOS33 } [get_ports { rst }]; #IO_L4P_T0_35 Sch=btn[0] 9 | 10 | ##HDMI Tx 11 | set_property -dict { PACKAGE_PIN L17 IOSTANDARD TMDS_33 } [get_ports { clk_n }]; #IO_L11N_T1_SRCC_35 Sch=hdmi_tx_clk_n 12 | set_property -dict { PACKAGE_PIN L16 IOSTANDARD TMDS_33 } [get_ports { clk_p }]; #IO_L11P_T1_SRCC_35 Sch=hdmi_tx_clk_p 13 | set_property -dict { PACKAGE_PIN K18 IOSTANDARD TMDS_33 } [get_ports { data_n[0] }]; #IO_L12N_T1_MRCC_35 Sch=hdmi_tx_d_n[0] 14 | set_property -dict { PACKAGE_PIN K17 IOSTANDARD TMDS_33 } [get_ports { data_p[0] }]; #IO_L12P_T1_MRCC_35 Sch=hdmi_tx_d_p[0] 15 | set_property -dict { PACKAGE_PIN J19 IOSTANDARD TMDS_33 } [get_ports { data_n[1] }]; #IO_L10N_T1_AD11N_35 Sch=hdmi_tx_d_n[1] 16 | set_property -dict { PACKAGE_PIN K19 IOSTANDARD TMDS_33 } [get_ports { data_p[1] }]; #IO_L10P_T1_AD11P_35 Sch=hdmi_tx_d_p[1] 17 | set_property -dict { PACKAGE_PIN H18 IOSTANDARD TMDS_33 } [get_ports { data_n[2] }]; #IO_L14N_T2_AD4N_SRCC_35 Sch=hdmi_tx_d_n[2] 18 | set_property -dict { PACKAGE_PIN J18 IOSTANDARD TMDS_33 } [get_ports { data_p[2] }]; #IO_L14P_T2_AD4P_SRCC_35 Sch=hdmi_tx_d_p[2] 19 | #set_property -dict { PACKAGE_PIN R19 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_hpdn }]; #IO_0_34 Sch=hdmi_tx_hpdn 20 | #set_property -dict { PACKAGE_PIN G15 IOSTANDARD LVCMOS33 } [get_ports { hdmi_tx_cec }]; #IO_L19N_T3_VREF_35 Sch=hdmi_tx_cec 21 | -------------------------------------------------------------------------------- /makefile: -------------------------------------------------------------------------------- 1 | # author: Furkan Cayci, 2018 2 | # description: 3 | # add ghdl to your PATH for simulation 4 | # add gtkwave to your PATH for displayin the waveform 5 | 6 | CC = ghdl 7 | SIM = gtkwave 8 | ARCHNAME = tb_hdmi_out 9 | STOPTIME = 4us 10 | 11 | # update Xilinx Vivado installation path 12 | XILINX_VIVADO ?= /opt/apps/Xilinx/Vivado/2019.1 13 | UNISIM_PATH = $(XILINX_VIVADO)/data/vhdl/src/unisims 14 | 15 | # use VHDL 2002 standard 16 | VHDLSTD = --std=02 17 | 18 | # order is important 19 | SRCS += rtl/clock_gen.vhd 20 | SRCS += rtl/serializer.vhd 21 | SRCS += rtl/tmds_encoder.vhd 22 | SRCS += rtl/timing_generator.vhd 23 | SRCS += rtl/rgb2tmds.vhd 24 | SRCS += rtl/pattern_generator.vhd 25 | SRCS += rtl/objectbuffer.vhd 26 | SRCS += rtl/hdmi_out.vhd 27 | 28 | TBS = $(wildcard sim/tb_*.vhd) 29 | TB = sim/$(ARCHNAME).vhd 30 | WORKDIR = debug 31 | 32 | # all the used primitives are added individually 33 | #UNISRCS += $(UNISIM_PATH)/*.vhd 34 | #UNISRCS += $(UNISIM_PATH)/primitive/*.vhd 35 | UNISRCS += $(UNISIM_PATH)/unisim_VCOMP.vhd 36 | UNISRCS += $(UNISIM_PATH)/unisim_VPKG.vhd 37 | UNISRCS += $(UNISIM_PATH)/primitive/BUFG.vhd 38 | UNISRCS += $(UNISIM_PATH)/primitive/OBUFDS.vhd 39 | UNISRCS += $(UNISIM_PATH)/primitive/PLLE2_ADV.vhd 40 | UNISRCS += $(UNISIM_PATH)/primitive/PLLE2_BASE.vhd 41 | # OSERDESE2 is encrypted IP core, and it cannot 42 | # be simulated using GHDL. Thus, we will downgrade 43 | # it to OSERDESE1 (from 6-series) 44 | UNISRCS += $(UNISIM_PATH)/primitive/OSERDESE1.vhd 45 | 46 | OBJS = $(patsubst sim/%.vhd, %.bin, $(TBS)) 47 | 48 | .PHONY: all 49 | all: clean analyze 50 | @echo "completed..." 51 | 52 | .PHONY: analyze 53 | analyze: 54 | @echo "analyzing designs..." 55 | @mkdir -p $(WORKDIR) 56 | $(CC) -a --work=unisim --workdir=$(WORKDIR) -fexplicit $(VHDLSTD) \ 57 | --ieee=synopsys $(UNISRCS) 58 | $(CC) -a --workdir=$(WORKDIR) -P$(WORKDIR) $(VHDLSTD) $(SRCS) $(TBS) 59 | 60 | .PHONY: simulate 61 | simulate: clean analyze 62 | @echo "simulating design:" $(TB) 63 | $(CC) --elab-run --workdir=$(WORKDIR) -P$(WORKDIR) $(VHDLSTD) -fexplicit \ 64 | --ieee=synopsys -o $(WORKDIR)/$(ARCHNAME).bin $(ARCHNAME) \ 65 | --vcd=$(WORKDIR)/$(ARCHNAME).vcd --stop-time=$(STOPTIME) 66 | $(SIM) $(WORKDIR)/$(ARCHNAME).vcd 67 | 68 | .PHONY: clean 69 | clean: 70 | @echo "cleaning design..." 71 | ghdl --remove --workdir=$(WORKDIR) 72 | rm -f $(WORKDIR)/* 73 | rm -rf $(WORKDIR) 74 | -------------------------------------------------------------------------------- /sim/tb_hdmi_out.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: hdmi out testbench 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | use ieee.numeric_std.all; 7 | 8 | entity tb_hdmi_out is 9 | end tb_hdmi_out; 10 | 11 | architecture rtl of tb_hdmi_out is 12 | 13 | signal clk : std_logic := '0'; 14 | signal rst : std_logic := '0'; 15 | constant clk_period : time := 8 ns; 16 | constant reset_time : time := 20 * clk_period; 17 | constant hsync_time : time := 1648 * clk_period; 18 | constant frame_time : time := 750 * hsync_time; 19 | 20 | -- interface ports / generics 21 | -- enable GHDL simulation support 22 | -- set this to false when using Vivado 23 | -- OSERDESE2 is normally used for 7-series 24 | -- but since it is encrypted, GHDL cannot simulate it 25 | -- Thus, this will downgrade it to OSERDESE1 26 | -- for simulation under GHDL 27 | constant SERIES6 : boolean := true; -- use OSERDES1/2 28 | constant RESOLUTION : string := "HD720P"; -- HD720P, SVGA, VGA 29 | constant GEN_PATTERN : boolean := false; -- generate pattern or objects 30 | constant GEN_PIX_LOC : boolean := true; -- generate location counters for x / y coordinates 31 | constant OBJECT_SIZE : natural := 16; -- size of the objects. should be higher than 11 32 | constant PIXEL_SIZE : natural := 24; -- RGB pixel total size. (R + G + B) 33 | 34 | signal clk_p, clk_n : std_logic; 35 | signal data_p, data_n : std_logic_vector(2 downto 0); 36 | 37 | begin 38 | 39 | uut0: entity work.hdmi_out 40 | generic map(RESOLUTION=>RESOLUTION, GEN_PATTERN=>GEN_PATTERN, 41 | GEN_PIX_LOC=>GEN_PIX_LOC, OBJECT_SIZE=>OBJECT_SIZE, 42 | PIXEL_SIZE=>PIXEL_SIZE, SERIES6=>SERIES6) 43 | port map(clk=>clk, rst=>rst, clk_p=>clk_p, clk_n=>clk_n, 44 | data_p=>data_p, data_n=>data_n); 45 | 46 | -- clock generate 47 | process 48 | begin 49 | --for i in 0 to 2 * frame_time / clk_period loop 50 | wait for clk_period/2; 51 | clk <= not clk; 52 | --end loop; 53 | --wait; 54 | end process; 55 | 56 | process 57 | begin 58 | -- report serdes status 59 | if SERIES6 then 60 | report "using OSERDES1 (for series 6)"; 61 | else 62 | report "using OSERDES2 (for series 7)"; 63 | end if; 64 | 65 | rst <= '1'; 66 | wait for reset_time; 67 | rst <= '0'; 68 | wait for frame_time; 69 | wait; 70 | end process; 71 | 72 | end rtl; 73 | -------------------------------------------------------------------------------- /rtl/pattern_generator.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: video pattern generator based on the active areas 3 | -- displays color spectrum 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.numeric_std.all; 8 | 9 | entity pattern_generator is 10 | port( 11 | clk : in std_logic; 12 | video_active : in std_logic; 13 | rgb : out std_logic_vector(23 downto 0) 14 | ); 15 | end pattern_generator; 16 | 17 | architecture rtl of pattern_generator is 18 | type state_type is (s0, s1, s2, s3, s4, s5); 19 | signal state : state_type := s0; 20 | 21 | signal r : unsigned(7 downto 0) := (others => '1'); 22 | signal b : unsigned(7 downto 0) := (others => '0'); 23 | signal g : unsigned(7 downto 0) := (others => '0'); 24 | begin 25 | 26 | rgb <= std_logic_vector(r & g & b); 27 | 28 | -- color spectrum process 29 | process(clk) is 30 | begin 31 | if rising_edge(clk) then 32 | if video_active = '1' then 33 | case state is 34 | when s0 => 35 | if g = 255 then 36 | state <= s1; 37 | else 38 | g <= g + 1; 39 | end if; 40 | when s1 => 41 | if r = 0 then 42 | state <= s2; 43 | else 44 | r <= r - 1; 45 | end if; 46 | when s2 => 47 | if b = 255 then 48 | state <= s3; 49 | else 50 | b <= b + 1; 51 | end if; 52 | when s3 => 53 | if g = 0 then 54 | state <= s4; 55 | else 56 | g <= g - 1; 57 | end if; 58 | when s4 => 59 | if r = 255 then 60 | state <= s5; 61 | else 62 | r <= r + 1; 63 | end if; 64 | when s5 => 65 | if b = 0 then 66 | state <= s0; 67 | else 68 | b <= b - 1; 69 | end if; 70 | end case; 71 | else 72 | r <= (others => '1'); 73 | g <= (others => '0'); 74 | b <= (others => '0'); 75 | state <= s0; 76 | end if; 77 | end if; 78 | end process; 79 | 80 | end rtl; 81 | -------------------------------------------------------------------------------- /rtl/rgb2tmds.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: generate tmds output based on the given rgb values and video timing 3 | -- used for DVI and HDMI signaling 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | 8 | entity rgb2tmds is 9 | generic ( 10 | SERIES6 : boolean := false 11 | ); 12 | port( 13 | -- reset and clocks 14 | rst : in std_logic; 15 | pixelclock : in std_logic; -- slow pixel clock 1x 16 | serialclock : in std_logic; -- fast serial clock 5x 17 | 18 | -- video signals 19 | video_data : in std_logic_vector(23 downto 0); 20 | video_active : in std_logic; 21 | hsync : in std_logic; 22 | vsync : in std_logic; 23 | 24 | -- tmds output ports 25 | clk_p : out std_logic; 26 | clk_n : out std_logic; 27 | data_p : out std_logic_vector(2 downto 0); 28 | data_n : out std_logic_vector(2 downto 0) 29 | ); 30 | end rgb2tmds; 31 | 32 | architecture rtl of rgb2tmds is 33 | signal enred, engreen, enblue : std_logic_vector(9 downto 0) := (others => '0'); 34 | signal sync : std_logic_vector(1 downto 0); 35 | 36 | begin 37 | 38 | sync <= vsync & hsync; 39 | 40 | -- tmds encoder 41 | tb : entity work.tmds_encoder(rtl) 42 | port map (clk=>pixelclock, en=>video_active, ctrl=>sync, din=>video_data(7 downto 0), dout=>enblue); 43 | tr : entity work.tmds_encoder(rtl) 44 | port map (clk=>pixelclock, en=>video_active, ctrl=>"00", din=>video_data(23 downto 16), dout=>enred); 45 | tg : entity work.tmds_encoder(rtl) 46 | port map (clk=>pixelclock, en=>video_active, ctrl=>"00", din=>video_data(15 downto 8), dout=>engreen); 47 | 48 | -- tmds output serializers 49 | ser_b: entity work.serializer(rtl) 50 | generic map (SERIES6=>SERIES6) 51 | port map (pixclk=>pixelclock, serclk=>serialclock, rst=>rst, endata_i=>enblue, s_p=>data_p(0), s_n=>data_n(0)); 52 | ser_g: entity work.serializer(rtl) 53 | generic map (SERIES6=>SERIES6) 54 | port map (pixclk=>pixelclock, serclk=>serialclock, rst=>rst, endata_i=>engreen, s_p=>data_p(1), s_n=>data_n(1)); 55 | ser_r: entity work.serializer(rtl) 56 | generic map (SERIES6=>SERIES6) 57 | port map (pixclk=>pixelclock, serclk=>serialclock, rst=>rst, endata_i=>enred, s_p=>data_p(2), s_n=>data_n(2)); 58 | -- tmds clock serializer to phase align with data signals 59 | ser_c: entity work.serializer(rtl) 60 | generic map (SERIES6=>SERIES6) 61 | port map (pixclk=>pixelclock, serclk=>serialclock, rst=>rst, endata_i=>"1111100000", s_p=>clk_p, s_n=>clk_n); 62 | 63 | end rtl; 64 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # vhdl-hdmi-out 2 | 3 | HDMI Out driver written in VHDL for 7-Series Xilinx FPGAs. Only video side is 4 | implemented and audio stuff is not included. Three resolutions are added. 5 | Serial cock runs at 5x the pixel clock and uses `OSERDES` blocks to generate 6 | the TMDS signals. Can be used to connect to a DVI input as well since they 7 | share the same protocol and timings. A `SERIES6` generic is added to replace 8 | `OSERDES2` blocks with `OSERDES1` blocks that are used in 6-Series. Since 9 | `OSERDES1` is not encrypted, GHDL can also elaborate and simulate the 10 | behavior. 11 | 12 | Project is configured to run at `125 Mhz` clock, but can be configurable from 13 | the [clock_gen.vhd](rtl/clock_gen.vhd) to run at different frequencies. 14 | [timing_generator.vhd](rtl/timing_generator.vhd) generates the the video timing 15 | signals: *hsync*, *vsync*, *video_active* as well as the pixel x and 16 | y locations. [pattern_generator.vhd](rtl/pattern_generator.vhd) and 17 | [objectbuffer.vhd](rtl/objectbuffer.vhd) are used to generate RGB pixel values 18 | and [rgb2tmds.vhd](rtl/rgb2tmds.vhd) is the top module that handles the 19 | conversion from RGB values to TMDS signal outputs. 20 | 21 | [GHDL](http://ghdl.free.fr/) can be used to simulate the circuit, and 22 | [GTKWave](http://gtkwave.sourceforge.net/) can be used to display the waveform. 23 | [makefile](makefile) is given to automate elaboration steps. It needs *Xilinx 24 | Vivado* installation to compile Xilinx supplied primitives such as `BUFG`, 25 | `OBUFDS` and `PLLE2`. However, `OSERDES2` that is used in the project is 26 | encrypted and cannot be imported to GHDL, so a generic is added to replace it 27 | with `OSERDES1` that is used in 6-Series devices. It is enabled by default in 28 | the simulation, and should be disabled if `OSERDES2` blocks are to be used. 29 | Update the relevant line with your Xilinx Vivado installation. (or run with 30 | `XILINX_VIVADO` environmental variable instead) 31 | 32 | ## Files 33 | 34 | ``` 35 | +- rtl/ 36 | | -- hdmi_out.vhd : hdmi out top module 37 | | -- clock_gen.vhd : generates pixel (1x) and serial (5x) clocks 38 | | -- timing_generator.vhd : generates timing signals for a given resolution 39 | | -- rgb2tmds.vhd : rgb to tmds parent module 40 | | -- tmds_encoder.vhd : 8b/10b rgb to tmds encoder 41 | | -- serializer.vhd : 10b tmds signal serializer to be sent out using serial (5x) clock 42 | | -- objectbuffer.vhd : rgb object generator based on active area 43 | | -- pattern_generator.vhd : rgb color spectrum pattern generator based on active area 44 | +- sim/ 45 | | -- tb_hdmi_out.vhd : testbench for hdmi out module 46 | +- imp/ 47 | | -- arty-z7.xdc : constraints for Arty-Z7 / Pynq-Z1 boards 48 | ``` 49 | 50 | ## Features 51 | 52 | * Supports `720p (1280x720)`, `SVGA (800x600)`, and `VGA (640x480)` modes. 53 | * Two patterns are included that are selectable on synthesizing 54 | * a rainbow color pattern - shows color spectrum 55 | * an object buffer for a game base - generates a wall, a box, and a ball. 56 | Wall is the stationary object, and box / ball are movable objects using 57 | the position signals. Background color can also be changed from top level 58 | * Tested on `Arty-Z7-20`, `Pynq-Z1` and `Pynq-Z2` boards. 59 | 60 | ## Misc. 61 | 62 | * There is an on/off problem on `Pynq-Z2` board when the clock is sourced from 63 | 125 Mhz PL clock. The display turns on for ~1 seconds and off for ~1 second. 64 | This problem goes away when the clock is generated from ZYNQ IO PLL. 65 | * [Yuri Cauwerts](https://github.com/zyuri) kindly pointed out that 66 | connecting Ethernet cable fixes the problem. 67 | * [tmds_encoder](rtl/tmds_encoder.vhd) usually needs pipelining for fixing 68 | timing problems. Usually I leave this to students to figure out how to 69 | implement it. 70 | * [vhdl-display-simulator](https://github.com/fcayci/vhdl-display-simulator) 71 | can be used to see the screen output without implementing your designs. 72 | -------------------------------------------------------------------------------- /rtl/tmds_encoder.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: 8b/10b based tmds encoder 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | use ieee.numeric_std.all; 7 | 8 | entity tmds_encoder is 9 | port ( 10 | clk : in std_logic; 11 | en : in std_logic; 12 | ctrl : in std_logic_vector(1 downto 0); 13 | din : in std_logic_vector(7 downto 0); 14 | dout : out std_logic_vector(9 downto 0) 15 | ); 16 | end tmds_encoder; 17 | 18 | architecture rtl of tmds_encoder is 19 | signal n_ones_din : integer range 0 to 8; 20 | 21 | signal xored, xnored : std_logic_vector(8 downto 0); 22 | signal q_m : std_logic_vector(8 downto 0); 23 | 24 | -- a positive value represents the excess number of 1's that have been transmitted 25 | -- a negative value represents the excess number of 0's that have been transmitted 26 | signal disparity : signed(3 downto 0) := to_signed(0, 4); 27 | -- difference between 1's and 0's (/2 since the last bit is never used) 28 | signal diff : signed(3 downto 0) := to_signed(0, 4); 29 | 30 | begin 31 | 32 | -- ones counter for input data 33 | process(din) is 34 | variable c : integer range 0 to 8; 35 | begin 36 | c := 0; 37 | for i in 0 to 7 loop 38 | if din(i) = '1' then 39 | c := c + 1; 40 | end if; 41 | end loop; 42 | n_ones_din <= c; 43 | end process; 44 | 45 | -- create xor encodings 46 | xored(0) <= din(0); 47 | encode_xor: for i in 1 to 7 generate 48 | begin 49 | xored(i) <= din(i) xor xored(i - 1); 50 | end generate; 51 | xored(8) <= '1'; 52 | 53 | -- create xnor encodings 54 | xnored(0) <= din(0); 55 | encode_xnor: for i in 1 to 7 generate 56 | begin 57 | xnored(i) <= din(i) xnor xnored(i - 1); 58 | end generate; 59 | xnored(8) <= '0'; 60 | 61 | -- use xnored or xored data based on the ones 62 | q_m <= xnored when n_ones_din > 4 or (n_ones_din = 4 and din(0) = '0') else xored; 63 | 64 | -- ones counter for internal data 65 | process(q_m) is 66 | variable c : integer range 0 to 8; 67 | begin 68 | c := 0; 69 | for i in 0 to 7 loop 70 | if q_m(i) = '1' then 71 | c := c + 1; 72 | end if; 73 | end loop; 74 | diff <= to_signed(c-4, 4); 75 | end process; 76 | 77 | process(clk) is 78 | begin 79 | if rising_edge(clk) then 80 | if en = '0' then 81 | case ctrl is 82 | when "00" => dout <= "1101010100"; 83 | when "01" => dout <= "0010101011"; 84 | when "10" => dout <= "0101010100"; 85 | when others => dout <= "1010101011"; 86 | end case; 87 | disparity <= (others => '0'); 88 | else 89 | if disparity = 0 or diff = 0 then 90 | -- xnored data 91 | if q_m(8) = '0' then 92 | dout <= "10" & not q_m(7 downto 0); 93 | disparity <= disparity - diff; 94 | -- xored data 95 | else 96 | dout <= "01" & q_m(7 downto 0); 97 | disparity <= disparity + diff; 98 | end if; 99 | elsif (diff(diff'left) = '0' and disparity(disparity'left) = '0') or 100 | (diff(diff'left) = '1' and disparity(disparity'left) = '1') then 101 | dout <= '1' & q_m(8) & not q_m(7 downto 0); 102 | if q_m(8) = '1' then 103 | disparity <= disparity + 1 - diff; 104 | else 105 | disparity <= disparity - diff; 106 | end if; 107 | else 108 | dout <= '0' & q_m; 109 | if q_m(8) = '1' then 110 | disparity <= disparity + diff; 111 | else 112 | disparity <= disparity - 1 + diff; 113 | end if; 114 | end if; 115 | end if; 116 | end if; 117 | end process; 118 | end rtl; -------------------------------------------------------------------------------- /rtl/objectbuffer.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: object buffer that holds the objects to display 3 | -- object locations can be controlled from upper level 4 | -- example contains a wall, a rectanble box and a round ball 5 | 6 | library ieee; 7 | use ieee.std_logic_1164.all; 8 | use ieee.numeric_std.all; 9 | 10 | entity objectbuffer is 11 | generic ( 12 | OBJECT_SIZE : natural := 16; 13 | PIXEL_SIZE : natural := 24; 14 | RES_X : natural := 1280; 15 | RES_Y : natural := 720 16 | ); 17 | port ( 18 | video_active : in std_logic; 19 | pixel_x, pixel_y : in std_logic_vector(OBJECT_SIZE-1 downto 0); 20 | object1x, object1y : in std_logic_vector(OBJECT_SIZE-1 downto 0); 21 | object2x, object2y : in std_logic_vector(OBJECT_SIZE-1 downto 0); 22 | backgrnd_rgb : in std_logic_vector(PIXEL_SIZE-1 downto 0); 23 | rgb : out std_logic_vector(PIXEL_SIZE-1 downto 0) 24 | ); 25 | end objectbuffer; 26 | 27 | architecture rtl of objectbuffer is 28 | -- create a 5 pixel vertical wall 29 | constant WALL_X_L: integer := 60; 30 | constant WALL_X_R: integer := 65; 31 | 32 | -- 1st object is a vertical box 48x8 pixel 33 | constant BOX_SIZE_X: integer := 8; 34 | constant BOX_SIZE_Y: integer := 48; 35 | -- x, y coordinates of the box 36 | signal box_x_l : unsigned (OBJECT_SIZE-1 downto 0); 37 | signal box_y_t : unsigned (OBJECT_SIZE-1 downto 0); 38 | signal box_x_r : unsigned (OBJECT_SIZE-1 downto 0); 39 | signal box_y_b : unsigned (OBJECT_SIZE-1 downto 0); 40 | 41 | -- 2nd object is a ball 42 | constant BALL_SIZE: integer:=8; 43 | type rom_type is array (0 to 7) of std_logic_vector(7 downto 0); 44 | 45 | constant BALL_ROM: rom_type := ( 46 | "00111100", -- **** 47 | "01111110", -- ****** 48 | "11111111", -- ******** 49 | "11111111", -- ******** 50 | "11111111", -- ******** 51 | "11111111", -- ******** 52 | "01111110", -- ****** 53 | "00111100" -- **** 54 | ); 55 | 56 | signal rom_addr, rom_col: unsigned(0 to 2); 57 | signal rom_bit: std_logic; 58 | -- x, y coordinates of the ball 59 | signal ball_x_l : unsigned(OBJECT_SIZE-1 downto 0); 60 | signal ball_y_t : unsigned(OBJECT_SIZE-1 downto 0); 61 | signal ball_x_r : unsigned(OBJECT_SIZE-1 downto 0); 62 | signal ball_y_b : unsigned(OBJECT_SIZE-1 downto 0); 63 | 64 | -- signals that holds the x, y coordinates 65 | signal pix_x, pix_y: unsigned (OBJECT_SIZE-1 downto 0); 66 | 67 | signal wall_on, box_on, square_ball_on, ball_on: std_logic; 68 | signal wall_rgb, box_rgb, ball_rgb: std_logic_vector(23 downto 0); 69 | 70 | begin 71 | 72 | pix_x <= unsigned(pixel_x); 73 | pix_y <= unsigned(pixel_y); 74 | 75 | -- draw wall and color 76 | wall_on <= '1' when WALL_X_L<=pix_x and pix_x<=WALL_X_R else '0'; 77 | wall_rgb <= x"0000FF"; -- blue 78 | 79 | -- draw box and color 80 | -- calculate the coordinates 81 | box_x_l <= unsigned(object1x); 82 | box_y_t <= unsigned(object1y); 83 | box_x_r <= box_x_l + BOX_SIZE_X - 1; 84 | box_y_b <= box_y_t + BOX_SIZE_Y - 1; 85 | box_on <= '1' when box_x_l<=pix_x and pix_x<=box_x_r and 86 | box_y_t<=pix_y and pix_y<=box_y_b else 87 | '0'; 88 | -- box rgb output 89 | box_rgb <= x"00FF00"; --green 90 | 91 | -- draw ball and color 92 | -- calculate the coordinates 93 | ball_x_l <= unsigned(object2x); 94 | ball_y_t <= unsigned(object2y); 95 | ball_x_r <= ball_x_l + BALL_SIZE - 1; 96 | ball_y_b <= ball_y_t + BALL_SIZE - 1; 97 | 98 | square_ball_on <= '1' when ball_x_l<=pix_x and pix_x<=ball_x_r and 99 | ball_y_t<=pix_y and pix_y<=ball_y_b else 100 | '0'; 101 | -- map current pixel location to ROM addr/col 102 | rom_addr <= pix_y(2 downto 0) - ball_y_t(2 downto 0); 103 | rom_col <= pix_x(2 downto 0) - ball_x_l(2 downto 0); 104 | rom_bit <= BALL_ROM(to_integer(rom_addr))(to_integer(rom_col)); 105 | -- pixel within ball 106 | ball_on <= '1' when square_ball_on='1' and rom_bit='1' else '0'; 107 | -- ball rgb output 108 | ball_rgb <= x"FF0000"; -- red 109 | 110 | -- display the image based on who is active 111 | -- note that the order is important 112 | process(video_active, wall_on, box_on, wall_rgb, box_rgb, ball_rgb, backgrnd_rgb, ball_on) is 113 | begin 114 | if video_active='0' then 115 | rgb <= x"000000"; --blank 116 | else 117 | if wall_on='1' then 118 | rgb <= wall_rgb; 119 | elsif ball_on='1' then 120 | rgb <= ball_rgb; 121 | elsif box_on='1' then 122 | rgb <= box_rgb; 123 | else 124 | rgb <= backgrnd_rgb; -- x"FFFF00"; -- yellow background 125 | end if; 126 | end if; 127 | end process; 128 | 129 | end rtl; -------------------------------------------------------------------------------- /rtl/hdmi_out.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: hdmi out top module 3 | -- consists of the timing module, clock manager and tgb to tdms encoder 4 | -- three different resolutions are added, selectable from the generic 5 | -- objectbuffer is added that displays 2 controllable 1 stationary objects 6 | -- optional pattern generator is added 7 | 8 | library ieee; 9 | use ieee.std_logic_1164.all; 10 | use ieee.numeric_std.all; 11 | library unisim; 12 | use unisim.vcomponents.all; 13 | 14 | entity hdmi_out is 15 | generic ( 16 | RESOLUTION : string := "HD1080P"; -- HD1080P, HD720P, SVGA, VGA 17 | GEN_PATTERN : boolean := false; -- generate pattern or objects 18 | GEN_PIX_LOC : boolean := true; -- generate location counters for x / y coordinates 19 | OBJECT_SIZE : natural := 16; -- size of the objects. should be higher than 11 20 | PIXEL_SIZE : natural := 24; -- RGB pixel total size. (R + G + B) 21 | SERIES6 : boolean := false -- disables OSERDESE2 and enables OSERDESE1 for GHDL simulation (7 series vs 6 series) 22 | ); 23 | port( 24 | clk, rst : in std_logic; 25 | -- tmds output ports 26 | clk_p : out std_logic; 27 | clk_n : out std_logic; 28 | data_p : out std_logic_vector(2 downto 0); 29 | data_n : out std_logic_vector(2 downto 0) 30 | ); 31 | end hdmi_out; 32 | 33 | architecture rtl of hdmi_out is 34 | 35 | signal pixclk, serclk : std_logic; 36 | signal video_active : std_logic := '0'; 37 | signal video_data : std_logic_vector(PIXEL_SIZE-1 downto 0); 38 | signal vsync, hsync : std_logic := '0'; 39 | signal pixel_x : std_logic_vector(OBJECT_SIZE-1 downto 0); 40 | signal pixel_y : std_logic_vector(OBJECT_SIZE-1 downto 0); 41 | signal object1x : std_logic_vector(OBJECT_SIZE-1 downto 0) := std_logic_vector(to_unsigned(500, OBJECT_SIZE)); 42 | signal object1y : std_logic_vector(OBJECT_SIZE-1 downto 0) := std_logic_vector(to_unsigned(140, OBJECT_SIZE)); 43 | signal object2x : std_logic_vector(OBJECT_SIZE-1 downto 0) := std_logic_vector(to_unsigned(240, OBJECT_SIZE)); 44 | signal object2y : std_logic_vector(OBJECT_SIZE-1 downto 0) := std_logic_vector(to_unsigned(340, OBJECT_SIZE)); 45 | signal backgrnd_rgb : std_logic_vector(PIXEL_SIZE-1 downto 0) := x"FFFF00"; -- yellow 46 | 47 | begin 48 | 49 | -- generate 1x pixel and 5x serial clocks 50 | timing_hd1080p: if RESOLUTION = "HD1080P" generate 51 | begin 52 | clock: entity work.clock_gen(rtl) 53 | generic map (CLKIN_PERIOD=>8.000, CLK_MULTIPLY=>59, CLK_DIVIDE=>5, CLKOUT0_DIV=>2, CLKOUT1_DIV=>10) -- 1080p 54 | port map (clk_i=>clk, clk0_o=>serclk, clk1_o=>pixclk); 55 | end generate; 56 | 57 | timing_hd720p: if RESOLUTION = "HD720P" generate 58 | begin 59 | clock: entity work.clock_gen(rtl) 60 | generic map (CLKIN_PERIOD=>8.000, CLK_MULTIPLY=>59, CLK_DIVIDE=>5, CLKOUT0_DIV=>4, CLKOUT1_DIV=>20) -- 720p 61 | port map (clk_i=>clk, clk0_o=>serclk, clk1_o=>pixclk); 62 | end generate; 63 | 64 | timing_vga: if RESOLUTION = "SVGA" generate 65 | begin 66 | clock: entity work.clock_gen(rtl) 67 | generic map (CLKIN_PERIOD=>8.000, CLK_MULTIPLY=>8, CLK_DIVIDE=>1, CLKOUT0_DIV=>5, CLKOUT1_DIV=>25) -- 800x600 68 | port map (clk_i=>clk, clk0_o=>serclk, clk1_o=>pixclk); 69 | end generate; 70 | 71 | timing_svga: if RESOLUTION = "VGA" generate 72 | begin 73 | clock: entity work.clock_gen(rtl) 74 | generic map (CLKIN_PERIOD=>8.000, CLK_MULTIPLY=>8, CLK_DIVIDE=>1, CLKOUT0_DIV=>8, CLKOUT1_DIV=>40) -- 640x480 75 | port map (clk_i=>clk, clk0_o=>serclk, clk1_o=>pixclk ); 76 | end generate; 77 | 78 | -- video timing 79 | timing: entity work.timing_generator(rtl) 80 | generic map (RESOLUTION => RESOLUTION, GEN_PIX_LOC => GEN_PIX_LOC, OBJECT_SIZE => OBJECT_SIZE) 81 | port map (clk=>pixclk, hsync=>hsync, vsync=>vsync, video_active=>video_active, pixel_x=>pixel_x, pixel_y=>pixel_y); 82 | 83 | -- tmds signaling 84 | tmds_signaling: entity work.rgb2tmds(rtl) 85 | generic map (SERIES6=>SERIES6) 86 | port map (rst=>rst, pixelclock=>pixclk, serialclock=>serclk, 87 | video_data=>video_data, video_active=>video_active, hsync=>hsync, vsync=>vsync, 88 | clk_p=>clk_p, clk_n=>clk_n, data_p=>data_p, data_n=>data_n); 89 | 90 | -- pattern generator 91 | gen_patt: if GEN_PATTERN = true generate 92 | begin 93 | pattern: entity work.pattern_generator(rtl) 94 | port map (clk=>pixclk, video_active=>video_active, rgb=>video_data); 95 | end generate; 96 | 97 | -- game object buffer 98 | gen_obj: if GEN_PATTERN = false generate 99 | begin 100 | objbuf: entity work.objectbuffer(rtl) 101 | generic map (OBJECT_SIZE=>OBJECT_SIZE, PIXEL_SIZE =>PIXEL_SIZE) 102 | port map (video_active=>video_active, pixel_x=>pixel_x, pixel_y=>pixel_y, 103 | object1x=>object1x, object1y=>object1y, 104 | object2x=>object2x, object2y=>object2y, 105 | backgrnd_rgb=>backgrnd_rgb, rgb=>video_data); 106 | end generate; 107 | 108 | end rtl; 109 | -------------------------------------------------------------------------------- /rtl/timing_generator.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: video timing generator 3 | -- choose between hd1080p, hd720p, svga, and vga 4 | 5 | library ieee; 6 | use ieee.std_logic_1164.all; 7 | use ieee.numeric_std.all; 8 | 9 | entity timing_generator is 10 | generic ( 11 | RESOLUTION : string := "HD720P"; -- hd1080p, hd720p, svga, vga 12 | GEN_PIX_LOC : boolean := true; 13 | OBJECT_SIZE : natural := 16 14 | ); 15 | port( 16 | clk : in std_logic; 17 | hsync, vsync : out std_logic; 18 | video_active : out std_logic; 19 | pixel_x : out std_logic_vector(OBJECT_SIZE-1 downto 0); 20 | pixel_y : out std_logic_vector(OBJECT_SIZE-1 downto 0) 21 | ); 22 | end timing_generator; 23 | 24 | architecture rtl of timing_generator is 25 | 26 | type video_timing_type is record 27 | H_VIDEO : integer; 28 | H_FP : integer; 29 | H_SYNC : integer; 30 | H_BP : integer; 31 | H_TOTAL : integer; 32 | V_VIDEO : integer; 33 | V_FP : integer; 34 | V_SYNC : integer; 35 | V_BP : integer; 36 | V_TOTAL : integer; 37 | H_POL : std_logic; 38 | V_POL : std_logic; 39 | ACTIVE : std_logic; 40 | end record; 41 | 42 | -- HD1080p timing 43 | -- screen area 1920x1080 @60 Hz 44 | -- horizontal : 1920 visible + 88 front porch (fp) + 44 hsync + 148 back porch = 2200 pixels 45 | -- vertical : 1080 visible + 4 front porch (fp) + 5 vsync + 36 back porch = 1125 pixels 46 | -- Total area 2200x1125 47 | -- clk input should be 148.5 MHz signal (2200 * 1125 * 60) 48 | -- hsync and vsync are positive polarity 49 | 50 | constant HD1080P_TIMING : video_timing_type := ( 51 | H_VIDEO => 1920, 52 | H_FP => 88, 53 | H_SYNC => 44, 54 | H_BP => 148, 55 | H_TOTAL => 2200, 56 | V_VIDEO => 1080, 57 | V_FP => 4, 58 | V_SYNC => 5, 59 | V_BP => 36, 60 | V_TOTAL => 1125, 61 | H_POL => '1', 62 | V_POL => '1', 63 | ACTIVE => '1' 64 | ); 65 | 66 | -- HD720p timing 67 | -- screen area 1280x720 @60 Hz 68 | -- horizontal : 1280 visible + 72 front porch (fp) + 80 hsync + 216 back porch = 1648 pixels 69 | -- vertical : 720 visible + 3 front porch (fp) + 5 vsync + 22 back porch = 750 pixels 70 | -- Total area 1648x750 71 | -- clk input should be 74.25 MHz signal (1650 * 750 * 60) 72 | -- hsync and vsync are positive polarity 73 | 74 | constant HD720P_TIMING : video_timing_type := ( 75 | H_VIDEO => 1280, 76 | H_FP => 72, 77 | H_SYNC => 80, 78 | H_BP => 216, 79 | H_TOTAL => 1648, 80 | V_VIDEO => 720, 81 | V_FP => 3, 82 | V_SYNC => 5, 83 | V_BP => 22, 84 | V_TOTAL => 750, 85 | H_POL => '1', 86 | V_POL => '1', 87 | ACTIVE => '1' 88 | ); 89 | 90 | -- SVGA timing 91 | -- screen area 800x600 @60 Hz 92 | -- horizontal : 800 visible + 40 front porch (fp) + 128 hsync + 88 back porch = 1056 pixels 93 | -- vertical : 600 visible + 1 front porch (fp) + 4 vsync + 23 back porch = 628 pixels 94 | -- Total area 1056x628 95 | -- clk input should be 40 MHz signal (1056 * 628 * 60) 96 | -- hsync and vsync are positive polarity 97 | 98 | constant SVGA_TIMING : video_timing_type := ( 99 | H_VIDEO => 800, 100 | H_FP => 40, 101 | H_SYNC => 128, 102 | H_BP => 88, 103 | H_TOTAL => 1056, 104 | V_VIDEO => 600, 105 | V_FP => 1, 106 | V_SYNC => 4, 107 | V_BP => 23, 108 | V_TOTAL => 628, 109 | H_POL => '1', 110 | V_POL => '1', 111 | ACTIVE => '1' 112 | ); 113 | 114 | -- VGA timing 115 | -- screen area 640x480 @60 Hz 116 | -- horizontal : 640 visible + 16 front porch (fp) + 96 hsync + 48 back porch = 800 pixels 117 | -- vertical : 480 visible + 10 front porch (fp) + 2 vsync + 33 back porch = 525 pixels 118 | -- Total area 800x525 119 | -- clk input should be 25 MHz signal (800 * 525 * 60) 120 | -- hsync and vsync are negative polarity 121 | 122 | constant VGA_TIMING : video_timing_type := ( 123 | H_VIDEO => 640, 124 | H_FP => 16, 125 | H_SYNC => 96, 126 | H_BP => 48, 127 | H_TOTAL => 800, 128 | V_VIDEO => 480, 129 | V_FP => 10, 130 | V_SYNC => 2, 131 | V_BP => 33, 132 | V_TOTAL => 525, 133 | H_POL => '0', 134 | V_POL => '0', 135 | ACTIVE => '1' 136 | ); 137 | 138 | -- horizontal and vertical counters 139 | signal hcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0'); 140 | signal vcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0'); 141 | signal timings : video_timing_type := HD720P_TIMING; 142 | 143 | begin 144 | 145 | timings <= HD1080P_TIMING when RESOLUTION = "HD1080P" else 146 | HD720P_TIMING when RESOLUTION = "HD720P" else 147 | SVGA_TIMING when RESOLUTION = "SVGA" else 148 | VGA_TIMING when RESOLUTION = "VGA"; 149 | 150 | -- pixel counters 151 | process (clk) is 152 | begin 153 | if rising_edge(clk) then 154 | if (hcount = timings.H_TOTAL) then 155 | hcount <= (others => '0'); 156 | if (vcount = timings.V_TOTAL) then 157 | vcount <= (others => '0'); 158 | else 159 | vcount <= vcount + 1; 160 | end if; 161 | else 162 | hcount <= hcount + 1; 163 | end if; 164 | end if; 165 | end process; 166 | 167 | -- generate video_active, hsync, and vsync signals based on the counters 168 | video_active <= timings.ACTIVE when (hcount < timings.H_VIDEO) and (vcount < timings.V_VIDEO ) else not timings.ACTIVE; 169 | hsync <= timings.H_POL when (hcount >= timings.H_VIDEO + timings.H_FP) and (hcount < timings.H_TOTAL - timings.H_BP) else not timings.H_POL; 170 | vsync <= timings.V_POL when (vcount >= timings.V_VIDEO + timings.V_FP) and (vcount < timings.V_TOTAL - timings.V_BP) else not timings.V_POL; 171 | 172 | g0: if GEN_PIX_LOC = true generate 173 | begin 174 | -- send pixel locations 175 | pixel_x <= std_logic_vector(hcount) when hcount < timings.H_VIDEO else (others => '0'); 176 | pixel_y <= std_logic_vector(vcount) when vcount < timings.V_VIDEO else (others => '0'); 177 | end generate; 178 | 179 | g1: if GEN_PIX_LOC = false generate 180 | begin 181 | -- send pixel locations 182 | pixel_x <= (others => '0'); 183 | pixel_y <= (others => '0'); 184 | end generate; 185 | end rtl; 186 | -------------------------------------------------------------------------------- /rtl/serializer.vhd: -------------------------------------------------------------------------------- 1 | -- author: Furkan Cayci, 2018 2 | -- description: serializer for for 10-bit tmds signal 3 | 4 | library ieee; 5 | use ieee.std_logic_1164.all; 6 | library unisim; 7 | use unisim.vcomponents.all; 8 | 9 | entity serializer is 10 | generic ( 11 | SERIES6 : boolean := false 12 | ); 13 | port ( 14 | rst : in std_logic; 15 | pixclk : in std_logic; -- low speed pixel clock 1x 16 | serclk : in std_logic; -- high speed serial clock 5x 17 | endata_i : in std_logic_vector(9 downto 0); 18 | s_p : out std_logic; 19 | s_n : out std_logic 20 | ); 21 | end serializer; 22 | 23 | architecture rtl of serializer is 24 | signal sdata : std_logic; 25 | signal cascade1, cascade2 : std_logic; 26 | begin 27 | 28 | -- create differential pair 29 | obuf : OBUFDS 30 | generic map (IOSTANDARD =>"TMDS_33") 31 | port map (I=>sdata, O=>s_p, OB=>s_n); 32 | 33 | -- generate for implementation 34 | fors7: if SERIES6 = false generate 35 | begin 36 | 37 | -- serializer 10:1 (5:1 DDR) 38 | -- master-slave cascaded since data width > 8 39 | master : OSERDESE2 40 | generic map ( 41 | DATA_RATE_OQ => "DDR", 42 | DATA_RATE_TQ => "SDR", 43 | DATA_WIDTH => 10, 44 | SERDES_MODE => "MASTER", 45 | TRISTATE_WIDTH => 1) 46 | port map ( 47 | OQ => sdata, 48 | OFB => open, 49 | TQ => open, 50 | TFB => open, 51 | SHIFTOUT1 => open, 52 | SHIFTOUT2 => open, 53 | TBYTEOUT => open, 54 | CLK => serclk, 55 | CLKDIV => pixclk, 56 | D1 => endata_i(0), 57 | D2 => endata_i(1), 58 | D3 => endata_i(2), 59 | D4 => endata_i(3), 60 | D5 => endata_i(4), 61 | D6 => endata_i(5), 62 | D7 => endata_i(6), 63 | D8 => endata_i(7), 64 | TCE => '0', 65 | OCE => '1', 66 | TBYTEIN => '0', 67 | RST => rst, 68 | SHIFTIN1 => cascade1, 69 | SHIFTIN2 => cascade2, 70 | T1 => '0', 71 | T2 => '0', 72 | T3 => '0', 73 | T4 => '0' 74 | ); 75 | 76 | slave : OSERDESE2 77 | generic map ( 78 | DATA_RATE_OQ => "DDR", 79 | DATA_RATE_TQ => "SDR", 80 | DATA_WIDTH => 10, 81 | SERDES_MODE => "SLAVE", 82 | TRISTATE_WIDTH => 1) 83 | port map ( 84 | OQ => open, 85 | OFB => open, 86 | TQ => open, 87 | TFB => open, 88 | SHIFTOUT1 => cascade1, 89 | SHIFTOUT2 => cascade2, 90 | TBYTEOUT => open, 91 | CLK => serclk, 92 | CLKDIV => pixclk, 93 | D1 => '0', 94 | D2 => '0', 95 | D3 => endata_i(8), 96 | D4 => endata_i(9), 97 | D5 => '0', 98 | D6 => '0', 99 | D7 => '0', 100 | D8 => '0', 101 | TCE => '0', 102 | OCE => '1', 103 | TBYTEIN => '0', 104 | RST => rst, 105 | SHIFTIN1 => '0', 106 | SHIFTIN2 => '0', 107 | T1 => '0', 108 | T2 => '0', 109 | T3 => '0', 110 | T4 => '0' 111 | ); 112 | 113 | end generate; 114 | 115 | -- WARNING: this is for GHDL simulator only. Not for implementation! 116 | -- OSERDESE2 is an encrypted IP, so convert that to 117 | -- OSERDESE1 (6-series serdes) 118 | fors6: if SERIES6 = true generate 119 | begin 120 | 121 | -- serializer 10:1 (5:1 DDR) 122 | -- master-slave cascaded since data width > 8 123 | master : OSERDESE1 124 | generic map ( 125 | DATA_RATE_OQ => "DDR", 126 | DATA_RATE_TQ => "SDR", 127 | DATA_WIDTH => 10, 128 | SERDES_MODE => "MASTER", 129 | TRISTATE_WIDTH => 1) 130 | port map ( 131 | OQ => sdata, 132 | OFB => open, 133 | TQ => open, 134 | TFB => open, 135 | SHIFTOUT1 => open, 136 | SHIFTOUT2 => open, 137 | CLKPERF => '0', 138 | CLKPERFDELAY => '0', 139 | ODV => '0', 140 | WC => '0', 141 | CLK => serclk, 142 | CLKDIV => pixclk, 143 | D1 => endata_i(0), 144 | D2 => endata_i(1), 145 | D3 => endata_i(2), 146 | D4 => endata_i(3), 147 | D5 => endata_i(4), 148 | D6 => endata_i(5), 149 | TCE => '0', 150 | OCE => '1', 151 | RST => rst, 152 | SHIFTIN1 => cascade1, 153 | SHIFTIN2 => cascade2, 154 | T1 => '0', 155 | T2 => '0', 156 | T3 => '0', 157 | T4 => '0' 158 | ); 159 | 160 | slave : OSERDESE1 161 | generic map ( 162 | DATA_RATE_OQ => "DDR", 163 | DATA_RATE_TQ => "SDR", 164 | DATA_WIDTH => 10, 165 | SERDES_MODE => "SLAVE", 166 | TRISTATE_WIDTH => 1) 167 | port map ( 168 | OQ => open, 169 | OFB => open, 170 | TQ => open, 171 | TFB => open, 172 | CLKPERF => '0', 173 | CLKPERFDELAY => '0', 174 | ODV => '0', 175 | WC => '0', 176 | SHIFTOUT1 => cascade1, 177 | SHIFTOUT2 => cascade2, 178 | CLK => serclk, 179 | CLKDIV => pixclk, 180 | D1 => '0', 181 | D2 => '0', 182 | D3 => endata_i(6), 183 | D4 => endata_i(7), 184 | D5 => endata_i(8), 185 | D6 => endata_i(9), 186 | TCE => '0', 187 | OCE => '1', 188 | RST => rst, 189 | SHIFTIN1 => '0', 190 | SHIFTIN2 => '0', 191 | T1 => '0', 192 | T2 => '0', 193 | T3 => '0', 194 | T4 => '0' 195 | ); 196 | 197 | end generate; 198 | 199 | end rtl; 200 | --------------------------------------------------------------------------------