What is the order of porches, visible video data, and sync periods in HDMI protocol?

32 Views Asked by At

I'm trying to make sense of the VHDL code from this github project. In particular, I try to understand this snippet:


-- author: Furkan Cayci, 2018
-- description: video timing generator
--      choose between hd1080p, hd720p, svga, and vga

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity timing_generator is
    generic (
        RESOLUTION   : string  := "HD720P"; -- hd1080p, hd720p, svga, vga
        GEN_PIX_LOC  : boolean := true;
        OBJECT_SIZE  : natural := 16
    );
    port(
        clk           : in  std_logic;
        hsync, vsync  : out std_logic;
        video_active  : out std_logic;
        pixel_x       : out std_logic_vector(OBJECT_SIZE-1 downto 0);
        pixel_y       : out std_logic_vector(OBJECT_SIZE-1 downto 0)
    );
end timing_generator;

architecture rtl of timing_generator is

    type video_timing_type is record
        H_VIDEO : integer;
        H_FP    : integer;
        H_SYNC  : integer;
        H_BP    : integer;
        H_TOTAL : integer;
        V_VIDEO : integer;
        V_FP    : integer;
        V_SYNC  : integer;
        V_BP    : integer;
        V_TOTAL : integer;
        H_POL   : std_logic;
        V_POL   : std_logic;
        ACTIVE  : std_logic;
    end record;

-- HD1080p timing
--      screen area 1920x1080 @60 Hz
--      horizontal : 1920 visible + 88 front porch (fp) + 44 hsync + 148 back porch = 2200 pixels
--      vertical   : 1080 visible +  4 front porch (fp) +  5 vsync +  36 back porch = 1125 pixels
--      Total area 2200x1125
--      clk input should be 148.5 MHz signal (2200 * 1125 * 60)
--      hsync and vsync are positive polarity

    constant HD1080P_TIMING : video_timing_type := (
        H_VIDEO => 1920,
        H_FP    =>   88,
        H_SYNC  =>   44,
        H_BP    =>  148,
        H_TOTAL => 2200,
        V_VIDEO => 1080,
        V_FP    =>    4,
        V_SYNC  =>    5,
        V_BP    =>   36,
        V_TOTAL => 1125,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

-- HD720p timing
--      screen area 1280x720 @60 Hz
--      horizontal : 1280 visible + 72 front porch (fp) + 80 hsync + 216 back porch = 1648 pixels
--      vertical   :  720 visible +  3 front porch (fp) +  5 vsync +  22 back porch =  750 pixels
--      Total area 1648x750
--      clk input should be 74.25 MHz signal (1650 * 750 * 60)
--      hsync and vsync are positive polarity

    constant HD720P_TIMING : video_timing_type := (
        H_VIDEO => 1280,
        H_FP    =>   72,
        H_SYNC  =>   80,
        H_BP    =>  216,
        H_TOTAL => 1648,
        V_VIDEO =>  720,
        V_FP    =>    3,
        V_SYNC  =>    5,
        V_BP    =>   22,
        V_TOTAL =>  750,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

-- SVGA timing
--      screen area 800x600 @60 Hz
--      horizontal : 800 visible + 40 front porch (fp) + 128 hsync + 88 back porch = 1056 pixels
--      vertical   : 600 visible +  1 front porch (fp) +   4 vsync + 23 back porch =  628 pixels
--      Total area 1056x628
--      clk input should be 40 MHz signal (1056 * 628 * 60)
--      hsync and vsync are positive polarity

    constant SVGA_TIMING : video_timing_type := (
        H_VIDEO =>  800,
        H_FP    =>   40,
        H_SYNC  =>  128,
        H_BP    =>   88,
        H_TOTAL => 1056,
        V_VIDEO =>  600,
        V_FP    =>    1,
        V_SYNC  =>    4,
        V_BP    =>   23,
        V_TOTAL =>  628,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

-- VGA timing
--      screen area 640x480 @60 Hz
--      horizontal : 640 visible + 16 front porch (fp) + 96 hsync + 48 back porch = 800 pixels
--      vertical   : 480 visible + 10 front porch (fp) +  2 vsync + 33 back porch = 525 pixels
--      Total area 800x525
--      clk input should be 25 MHz signal (800 * 525 * 60)
--      hsync and vsync are negative polarity

    constant VGA_TIMING : video_timing_type := (
        H_VIDEO =>  640,
        H_FP    =>   16,
        H_SYNC  =>   96,
        H_BP    =>   48,
        H_TOTAL =>  800,
        V_VIDEO =>  480,
        V_FP    =>   10,
        V_SYNC  =>    2,
        V_BP    =>   33,
        V_TOTAL =>  525,
        H_POL   =>  '0',
        V_POL   =>  '0',
        ACTIVE  =>  '1'
    );

    -- horizontal and vertical counters
    signal hcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0');
    signal vcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0');
    signal timings : video_timing_type := HD720P_TIMING;

begin

    timings <= HD1080P_TIMING when RESOLUTION = "HD1080P" else
               HD720P_TIMING when RESOLUTION = "HD720P" else
               SVGA_TIMING   when RESOLUTION = "SVGA"   else
               VGA_TIMING    when RESOLUTION = "VGA";

    -- pixel counters
    process (clk) is
    begin
        if rising_edge(clk) then
            if (hcount = timings.H_TOTAL) then
                hcount <= (others => '0');
                if (vcount = timings.V_TOTAL) then
                    vcount <= (others => '0');
                else
                    vcount <= vcount + 1;
                end if;
            else
                hcount <= hcount + 1;
            end if;
        end if;
    end process;

    -- generate video_active, hsync, and vsync signals based on the counters
    video_active <= timings.ACTIVE when (hcount < timings.H_VIDEO) and (vcount < timings.V_VIDEO ) else not timings.ACTIVE;
    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;
    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;

    g0: if GEN_PIX_LOC = true generate
    begin
        -- send pixel locations
        pixel_x <= std_logic_vector(hcount) when hcount < timings.H_VIDEO else (others => '0');
        pixel_y <= std_logic_vector(vcount) when vcount < timings.V_VIDEO else (others => '0');
    end generate;

    g1: if GEN_PIX_LOC = false generate
    begin
        -- send pixel locations
        pixel_x <= (others => '0');
        pixel_y <= (others => '0');
    end generate;
end rtl;
 

My interpretation of the code (taking into consideration what I know about HDMI protocol and vhdl) is given in the commented version below:

library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity timing_generator is
    -- this are pased and used at compile time only
    generic (
        RESOLUTION   : string  := "HD720P"; 
        GEN_PIX_LOC  : boolean := true;
        OBJECT_SIZE  : natural := 16
    );
    -- this are signals which will be instantiated in hardware
    port(
        clk           : in  std_logic;
        hsync, vsync  : out std_logic;
        video_active  : out std_logic;
        pixel_x       : out std_logic_vector(OBJECT_SIZE-1 downto 0);
        pixel_y       : out std_logic_vector(OBJECT_SIZE-1 downto 0)
    );
end timing_generator;

architecture rtl of timing_generator is

    -- this is just a struct definition, only relevant at compile time
    type video_timing_type is record
        H_VIDEO : integer;
        H_FP    : integer;
        H_SYNC  : integer;
        H_BP    : integer;
        H_TOTAL : integer;
        V_VIDEO : integer;
        V_FP    : integer;
        V_SYNC  : integer;
        V_BP    : integer;
        V_TOTAL : integer;
        H_POL   : std_logic;
        V_POL   : std_logic;
        ACTIVE  : std_logic;
    end record;

    -- everything below are specifications of region sizes for different resolutions
    -- they are probably specified in HDMI protocol tech spec
    -- they are only defined here for one to be instantiated at compile time

    constant HD1080P_TIMING : video_timing_type := (
        H_VIDEO => 1920,
        H_FP    =>   88,
        H_SYNC  =>   44,
        H_BP    =>  148,
        H_TOTAL => 2200,
        V_VIDEO => 1080,
        V_FP    =>    4,
        V_SYNC  =>    5,
        V_BP    =>   36,
        V_TOTAL => 1125,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

    constant HD720P_TIMING : video_timing_type := (
        H_VIDEO => 1280,
        H_FP    =>   72,
        H_SYNC  =>   80,
        H_BP    =>  216,
        H_TOTAL => 1648,
        V_VIDEO =>  720,
        V_FP    =>    3,
        V_SYNC  =>    5,
        V_BP    =>   22,
        V_TOTAL =>  750,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

    constant SVGA_TIMING : video_timing_type := (
        H_VIDEO =>  800,
        H_FP    =>   40,
        H_SYNC  =>  128,
        H_BP    =>   88,
        H_TOTAL => 1056,
        V_VIDEO =>  600,
        V_FP    =>    1,
        V_SYNC  =>    4,
        V_BP    =>   23,
        V_TOTAL =>  628,
        H_POL   =>  '1',
        V_POL   =>  '1',
        ACTIVE  =>  '1'
    );

    constant VGA_TIMING : video_timing_type := (
        H_VIDEO =>  640,
        H_FP    =>   16,
        H_SYNC  =>   96,
        H_BP    =>   48,
        H_TOTAL =>  800,
        V_VIDEO =>  480,
        V_FP    =>   10,
        V_SYNC  =>    2,
        V_BP    =>   33,
        V_TOTAL =>  525,
        H_POL   =>  '0',
        V_POL   =>  '0',
        ACTIVE  =>  '1'
    );

    -- this will be a register storing OBJECT_SIZE bits. It will be interpreted as unsigned int.
    -- it is zeroed out at the start
    signal hcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0');
    -- likewise
    signal vcount : unsigned(OBJECT_SIZE-1 downto 0) := (others => '0');
    -- it's likely set here to arbitrary timing because it cannot be left unset.
    signal timings : video_timing_type := HD720P_TIMING;

begin
    -- this is where preproc sets timing based on generic param
    timings <= HD1080P_TIMING when RESOLUTION = "HD1080P" else
               HD720P_TIMING when RESOLUTION = "HD720P" else
               SVGA_TIMING   when RESOLUTION = "SVGA"   else
               VGA_TIMING    when RESOLUTION = "VGA";

    -- this are basically two for loops iterating over x and y values
    -- since each process is executed on it's own, it's synced on clock and 
    -- provides coordinates of currently rendered pixel for other components
    process (clk) is
    begin
        if rising_edge(clk) then
            if (hcount = timings.H_TOTAL) then -- if end of row
                hcount <= (others => '0');      -- reset x coordinate
                if (vcount = timings.V_TOTAL) then  -- if end of frame
                    vcount <= (others => '0');       -- reset y coordinate
                else                                
                    vcount <= vcount + 1;            -- else, increment y coordinate
                end if;
            else
                hcount <= hcount + 1;          -- else, increment x coordinate     
            end if;
        end if;
    end process;

    -- since not every "pixel" is actual graphical data flags are set depending on what region is being rendered
    -- video active is set when x is less the visible frame width  and y is less then visible frame height
    video_active <= timings.ACTIVE when (hcount < timings.H_VIDEO) and (vcount < timings.V_VIDEO ) else not timings.ACTIVE;
    -- hsync and vsync are set when horz_sync region is traversed and vert_sync region is traversed respectively
    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;
    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;

    -- other modules may use the locations in the visible frame to determine the rgb data
    -- broadcast those coordinates is conditionaly enabled at compile time
    g0: if GEN_PIX_LOC = true generate
    begin
        -- broadcast x coordinate when x is less then visible frame width, otherwise broadcast zero
        pixel_x <= std_logic_vector(hcount) when hcount < timings.H_VIDEO else (others => '0');
        -- broadcast y coordinate when y is less then visible frame height, otherwise broadcast zero
        pixel_y <= std_logic_vector(vcount) when vcount < timings.V_VIDEO else (others => '0');
    end generate;

    g1: if GEN_PIX_LOC = false generate
    begin
        pixel_x <= (others => '0');
        pixel_y <= (others => '0');
    end generate;
end rtl;

From the comments in the original code I assumed that transmittion of an individual frame looks as follows:

  • In horizontal direction: Front Porch, Rendered Video, Sync Region, Back Porch.

  • In vertical direction: Front Porch, Rendered Video, Sync Region, Back Porch.


However, few things confuse me, forcing me to doubt whether this is indeed the order in which data is send over the link.

If front porch perceeds rendered video both vertically and horizontally and hcount and vcount span all the data space, not just the rendered frame, why video_active flag is set with

video_active <= timings.ACTIVE when (hcount < timings.H_VIDEO) and (vcount < timings.V_VIDEO ) else not timings.ACTIVE;

and not with

video_active <= timings.ACTIVE when (hcount > timings.H_FP) and (hcount < timings.H_VIDEO + timings.H_FP) and (vcount > timings.V_FP) and (vcount < timings.V_VIDEO + V_FP) else not timings.ACTIVE;

Similarly, why when broadcasting coordinates of currently rendered pixel, the values are not offset to take front porch into consideration. Moreover, why are conditions for broadcasting x and y only depend on the respective coordinate. That would imply that x will be boradcast during the vertical front and back porches and vice versa. In the module which makes use of the coordinates no offsets are applied either.

Finally, location and purpose of sync region is unclear to me. While HDMI protocol specifies that porches should be used to place data and control islands, I do not understand the purpose of sync regions. Sync flags are only transmit during control islands, hence receiver can only decode it after receiving a preambule notifying it that now begins the data island. But if the receiver knows that now begins the data island, what is the point of notifying it about completing row or frame?


To illustrate first two of my doubts, here is a diagram:

enter image description here

0

There are 0 best solutions below