0
\$\begingroup\$

When I studied the GPIO demo code for Basys3 to create my own VGA controller, I noticed that multiple registers are used for a single signal, like h_cntr, H_sync, V_sync, VGA_R, VGA_G, VGA_B, etc. I am wondering why there is such a need for introducing a delay in a VGA controller circuit so that I can understand that well and make my own component for controlling a VGA monitor. The code is as follows.

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.STD_LOGIC_ARITH.ALL;
USE IEEE.std_logic_unsigned.ALL;
USE ieee.math_real.ALL;



ENTITY vga_ctrl IS
    PORT (
        CLK_I : IN STD_LOGIC;
        VGA_HS_O : OUT STD_LOGIC;
        VGA_VS_O : OUT STD_LOGIC;
        VGA_RED_O : OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
        VGA_BLUE_O : OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
        VGA_GREEN_O : OUT STD_LOGIC_VECTOR (3 DOWNTO 0);
        PS2_CLK : INOUT STD_LOGIC;
        PS2_DATA : INOUT STD_LOGIC
    );
END vga_ctrl;

ARCHITECTURE Behavioral OF vga_ctrl IS

    COMPONENT clk_wiz_0
        PORT (
            clk_in1 : IN std_logic;

            clk_out1 : OUT std_logic
        );
    END COMPONENT;

    CONSTANT FRAME_WIDTH : NATURAL := 1280;
    CONSTANT FRAME_HEIGHT : NATURAL := 1024;
 
    CONSTANT H_FP : NATURAL := 48; 
    CONSTANT H_PW : NATURAL := 112; 
    CONSTANT H_MAX : NATURAL := 1688; 
 
    CONSTANT V_FP : NATURAL := 1; 
    CONSTANT V_PW : NATURAL := 3; 
    CONSTANT V_MAX : NATURAL := 1066; 
 
    CONSTANT H_POL : std_logic := '1';
    CONSTANT V_POL : std_logic := '1';
 
    SIGNAL pxl_clk : std_logic;

    SIGNAL active : std_logic;

    SIGNAL h_cntr_reg : std_logic_vector(11 DOWNTO 0) := (OTHERS => '0');
    SIGNAL v_cntr_reg : std_logic_vector(11 DOWNTO 0) := (OTHERS => '0');
 
    SIGNAL h_cntr_reg_dly : std_logic_vector(11 DOWNTO 0) := (OTHERS => '0');
    SIGNAL v_cntr_reg_dly : std_logic_vector(11 DOWNTO 0) := (OTHERS => '0');
 
    SIGNAL h_sync_reg : std_logic := NOT(H_POL);
    SIGNAL v_sync_reg : std_logic := NOT(V_POL);

    SIGNAL h_sync_reg_dly : std_logic := NOT(H_POL);
    SIGNAL v_sync_reg_dly : std_logic := NOT(V_POL);
 
    SIGNAL vga_red_cmb : std_logic_vector(3 DOWNTO 0);
    SIGNAL vga_green_cmb : std_logic_vector(3 DOWNTO 0);
    SIGNAL vga_blue_cmb : std_logic_vector(3 DOWNTO 0);

    SIGNAL vga_red : std_logic_vector(3 DOWNTO 0);
    SIGNAL vga_green : std_logic_vector(3 DOWNTO 0);
    SIGNAL vga_blue : std_logic_vector(3 DOWNTO 0);

    SIGNAL vga_red_reg : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
    SIGNAL vga_green_reg : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
    SIGNAL vga_blue_reg : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
 
    SIGNAL cntDyn : INTEGER RANGE 0 TO 2 ** 28 - 1; 
    SIGNAL intHcnt : INTEGER RANGE 0 TO H_MAX - 1;
    SIGNAL intVcnt : INTEGER RANGE 0 TO V_MAX - 1;

    SIGNAL bg_red : std_logic_vector(3 DOWNTO 0);
    SIGNAL bg_blue : std_logic_vector(3 DOWNTO 0);
    SIGNAL bg_green : std_logic_vector(3 DOWNTO 0);

    SIGNAL bg_red_dly : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
    SIGNAL bg_green_dly : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
    SIGNAL bg_blue_dly : std_logic_vector(3 DOWNTO 0) := (OTHERS => '0');
 
BEGIN
    clk_wiz_0_inst : clk_wiz_0
        PORT MAP (
            clk_in1 => CLK_I,
            clk_out1 => pxl_clk
        );

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN
            IF (h_cntr_reg = (H_MAX - 1)) THEN
                h_cntr_reg <= (OTHERS => '0');
            ELSE
                h_cntr_reg <= h_cntr_reg + 1;
            END IF;
        END IF;
    END PROCESS;

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN
            IF ((h_cntr_reg = (H_MAX - 1)) AND (v_cntr_reg = (V_MAX - 1))) THEN
                v_cntr_reg <= (OTHERS => '0');
            ELSIF (h_cntr_reg = (H_MAX - 1)) THEN
                v_cntr_reg <= v_cntr_reg + 1;
            END IF;
        END IF;
    END PROCESS;

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN
            IF (h_cntr_reg >= (H_FP + FRAME_WIDTH - 1)) AND (h_cntr_reg < (H_FP + FRAME_WIDTH + H_PW - 1)) THEN
                h_sync_reg <= H_POL;
            ELSE
                h_sync_reg <= NOT(H_POL);
            END IF;
        END IF;
    END PROCESS;

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN
            IF (v_cntr_reg >= (V_FP + FRAME_HEIGHT - 1)) AND (v_cntr_reg < (V_FP + FRAME_HEIGHT + V_PW - 1)) THEN
                v_sync_reg <= V_POL;
            ELSE
                v_sync_reg <= NOT(V_POL);
            END IF;
        END IF;
    END PROCESS;

    active <= '1' WHEN h_cntr_reg_dly < FRAME_WIDTH AND v_cntr_reg_dly < FRAME_HEIGHT
        ELSE '0';

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN
            cntdyn <= cntdyn + 1;
        END IF;
    END PROCESS;

    intHcnt <= conv_integer(h_cntr_reg);
    intVcnt <= conv_integer(v_cntr_reg);

    bg_red <= conv_std_logic_vector(( - intvcnt - inthcnt - cntDyn/2 ** 20), 8)(7 DOWNTO 4);
    bg_green <= conv_std_logic_vector((inthcnt - cntDyn/2 ** 20), 8)(7 DOWNTO 4);
    bg_blue <= conv_std_logic_vector((intvcnt - cntDyn/2 ** 20), 8)(7 DOWNTO 4);

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN

            bg_red_dly <= bg_red;
            bg_green_dly <= bg_green;
            bg_blue_dly <= bg_blue;

            h_cntr_reg_dly <= h_cntr_reg;
            v_cntr_reg_dly <= v_cntr_reg;

        END IF;
    END PROCESS;

    vga_red <= mouse_cursor_red_dly WHEN enable_mouse_display_dly = '1' ELSE
                                             bg_red_dly;
    vga_green <= mouse_cursor_green_dly WHEN enable_mouse_display_dly = '1' ELSE
                                             bg_green_dly;
    vga_blue <= mouse_cursor_blue_dly WHEN enable_mouse_display_dly = '1' ELSE
                                             bg_blue_dly;

    vga_red_cmb <= (active & active & active & active) AND vga_red;
    vga_green_cmb <= (active & active & active & active) AND vga_green;
    vga_blue_cmb <= (active & active & active & active) AND vga_blue;

    PROCESS (pxl_clk)
    BEGIN
        IF (rising_edge(pxl_clk)) THEN

            v_sync_reg_dly <= v_sync_reg;
            h_sync_reg_dly <= h_sync_reg;
            vga_red_reg <= vga_red_cmb;
            vga_green_reg <= vga_green_cmb;
            vga_blue_reg <= vga_blue_cmb;
        END IF;
    END PROCESS;

    VGA_HS_O <= h_sync_reg_dly;
    VGA_VS_O <= v_sync_reg_dly;
    VGA_RED_O <= vga_red_reg;
    VGA_GREEN_O <= vga_green_reg;
    VGA_BLUE_O <= vga_blue_reg;

END Behavioral;
\$\endgroup\$
2
  • \$\begingroup\$ Which registers do you refer to exactly? E.g. on vga_red_* I see only one register in your code. \$\endgroup\$
    – asdfex
    Apr 5 at 10:01
  • \$\begingroup\$ h_sync_reg and h_sync_reg_dly, v_sync_reg and v_sync_reg_dly etc. \$\endgroup\$
    – FPGAVHDL
    Apr 5 at 14:39

1 Answer 1

3
\$\begingroup\$

Assuming a 60-Hz frame rate, your pixel clock is approximately 108 MHz, which is a period of just 9.2 ns.

The pipeline registers are there to address the reality that the FPGA resources (LUTs) can only do a certain amount of logic in that amount of time. The computations involved in computing the pixel values are captured in bg_red_dly, bg_green_dly, bg_blue_dly, and the subsequent logic to do the mouse overlay and active-area masking is captured in vga_red_reg, vga_green_reg, vga_blue_reg. The sync signals are also pipelined, in order to keep them lined up in time properly with the pixels.

That said, there are obvious optimizations that could be done here. For example, instead of delaying the counter values to compute active, it could be computed using the non-delayed counters, and then pipeline the result — a single bit instead of two 12-bit values. Also, the sync signals could have been delayed by simply adjusting the counter values by which they're generated.

\$\endgroup\$

Not the answer you're looking for? Browse other questions tagged or ask your own question.