Search code examples
buffervhdlclockxilinx-isespartan

Using BUFG to drive clock loads


I'm attempting to work with pixel data that is output to a DVI chip. A variety of clock frequencies are used because the DVI chip registers are programmed using I2C (therefore needs a clock < 500 KHz) - from a clock divider.

The DVI chip needs a 40 MHz differential pixel clock, however, the DVI takes displays half a byte twice a cycle, so a further 80 MHz clock is needed to push each half of the pixel onto the data lines with each half cycle, these are from a DCM.

This has led to a variety of problems. I attempted to just use the double pixel clock rate to swap each half of the pixel, however I got the error:

This design contains a global buffer instance, <out2_bufg>, driving the net, <pxlclk_p_int>, that is driving the following (first 30) non-clock load pins.

So I added a BUFG element between the output of the DCM and the components using the signal - but it hasn't changed anything, instead the error is now thrown twice on both the input and output of the BUFG.

  • How can I fix this - given I've just added a BUFG and it doesn't like it!

My code is below; I've tried to cut out the unrelated things that aren't related to the clock but its still quite long!

EDIT 1: I have added the block, which when added to the system, caused the error (that wasn't there before) Its in the second block of code! I am currently working on the other suggestions RE the differential signalling, and will edit again when done!

Thanks very much,

David

LIBRARY ieee;
USE ieee.std_logic_1164.ALL;
USE ieee.numeric_std.ALL;
library UNISIM;
use UNISIM.VComponents.all;

ENTITY I2CBus IS
    PORT(
        SYSCLK_N   : IN    STD_LOGIC;   --system 200MHz differential clock
        SYSCLK_P   : IN    STD_LOGIC;
        BTN        : IN    STD_LOGIC;   -- to manually change reset

        LED        : OUT   STD_LOGIC_VECTOR(3 downto 0); --to observe reset value
        SCL_DBG    : OUT   STD_LOGIC;   -- copy of SCL to output pin
        SDA_DBG    : OUT   STD_LOGIC;   --copy of SDA to output pin
        SCL        : OUT   STD_LOGIC;   --Serial Clock Line
        SDA        : INOUT STD_LOGIC;   --Serial Data Line

        DVIRESET_N : OUT   STD_LOGIC;   --reset_n to dvi device
        DVI_ENABLE : OUT   STD_LOGIC;   --enable DVI device inputs (active high)
        PXLCLK_P   : OUT   STD_LOGIC;   --pixel clock differential pair through buffers
        PXLCLK_N   : OUT   STD_LOGIC;
        DVI_DATA   : OUT   STD_LOGIC_VECTOR(11 downto 0); --12 bit multiplexed pixel to DVI
        HSYNC      : OUT   STD_LOGIC;   --Horizontal/Vertical sync timing pulses
        VSYNC      : OUT   STD_LOGIC
    );
END I2CBus;
ARCHITECTURE behavior OF I2CBus IS
    COMPONENT IIC_MASTER                --sends data to write out onto SDA bus line in I2C protocol
        PORT(SCL     : IN    STD_LOGIC;
             SCL2X   : IN    STD_LOGIC;
             RESET_N : IN    STD_LOGIC;
             ENA     : IN    STD_LOGIC;
             ADR     : IN    STD_LOGIC_VECTOR(6 DOWNTO 0);
             REG     : IN    STD_LOGIC_VECTOR(7 DOWNTO 0);
             RW      : IN    STD_LOGIC;
             DAT_WR  : IN    STD_LOGIC_VECTOR(7 DOWNTO 0);
             BUSY    : OUT   STD_LOGIC;
             SDA     : INOUT STD_LOGIC;
             ACK_ERR : BUFFER STD_LOGIC);
    END COMPONENT IIC_MASTER;
    COMPONENT DCM                       --takes input system differential clocks, generates further clocks
        PORT(
            SYSCLK_P : IN  STD_LOGIC;   -- CLOCK IN PORTS 200MHZ DIFFERENTIAL
            SYSCLK_N : IN  STD_LOGIC;
            -- CLOCK OUT PORTS
            SYSCLK   : OUT STD_LOGIC;
            PXLCLK   : OUT STD_LOGIC;
            PXLCLK2X : OUT STD_LOGIC
        );
    END COMPONENT;

    COMPONENT CLK_DIVIDER               --divides system clock down for i2c bus clock line
        GENERIC(INPUT_FREQ : INTEGER;
                OUT1_FREQ  : INTEGER;
                OUT2_FREQ  : INTEGER);
        PORT(SYSCLK      : IN  STD_LOGIC;
             RESET_N     : IN  STD_LOGIC;
             RESET_N_OUT : OUT STD_LOGIC;
             OUT1        : OUT STD_LOGIC;
             OUT2        : OUT STD_LOGIC);
    END COMPONENT CLK_DIVIDER;

    COMPONENT DVI_INITIALISE            --initialises CH7301c registers to necessary operation values
        PORT(SYSCLK      : IN  STD_LOGIC;
             ACK_ERR     : IN  STD_LOGIC;
             BUSY        : IN  STD_LOGIC;
             RESET_N     : IN  STD_LOGIC;
             COUNT       : OUT STD_LOGIC_VECTOR(3 DOWNTO 0);
             DVI_WR      : OUT STD_LOGIC := '0';
             DVI_REGDATA : OUT STD_LOGIC_VECTOR(7 DOWNTO 0);
             DVI_WDATA   : OUT STD_LOGIC_VECTOR(7 DOWNTO 0));
    END COMPONENT DVI_INITIALISE;

    COMPONENT DVI_INTERFACE             --outputs sync pulses, controls enable and manages pixel addresses
        PORT(PIXEL_CLK    : IN  STD_LOGIC;
             RESET_N      : IN  STD_LOGIC;
             PXL_ADDR     : OUT STD_LOGIC_VECTOR(19 DOWNTO 0) := (OTHERS => '0');
             HSYNC, VSYNC : OUT STD_LOGIC                     := '1';
             ENABLE       : OUT STD_LOGIC                     := '0');
    END COMPONENT DVI_INTERFACE;

    COMPONENT DVI_MUX
        PORT(PXLCLK   : IN  STD_LOGIC;
             PXLCLK2X : IN  STD_LOGIC;
             PXL_DAT  : IN  STD_LOGIC_VECTOR(23 DOWNTO 0); --pixel as RGB
             DATA     : OUT STD_LOGIC_VECTOR(11 DOWNTO 0); --multiplexed output
             RESET_N  : IN  STD_LOGIC); --reset low signal
    END COMPONENT DVI_MUX;

    --Inputs
    signal reset_n_input : std_logic;   -- input reset from button

    ----Outputs ------
    signal sda_internal : STD_LOGIC;    -- Internal SDA

    ----Clocks-----
    signal SCL_internal   : std_logic;  -- i2c clock
    signal SCL2X_internal : std_logic;  -- i2c x2 to load SDA data
    signal sysclk         : std_logic;  --system clock
    signal pxlclk_p_int   : std_logic;  --differential pixel clock pair
    signal pxlclk_n_int   : std_logic;
    signal pxlclk         : std_logic;  --pxlclk after BUFG
    signal pxlclk2x_int   : STD_LOGIC;  --2x pixel clock for loading pixel data

    -----Internal Control Signals ---
    signal reset_n : std_logic;         --active high
    signal busy    : std_logic;         --low when not i2c not busy
    signal ack_err : std_logic;         --high when i2c ackknowledge error occurs

    ----Internal Data-----
    signal i2c_reg  : STD_LOGIC_VECTOR(7 DOWNTO 0); --register data for I2C
    signal i2c_rw   : STD_LOGIC;        --R/W* for I2C
    signal i2c_data : STD_LOGIC_VECTOR(7 DOWNTO 0); --Data for I2C

BEGIN
    master : IIC_Master
        port map(
            SCL     => SCL_internal,
            SCL2X   => SCL2X_internal,
            RESET_N => RESET_N,
            ENA     => '1',
            ADR     => "1110110",
            REG     => i2c_reg,
            RW      => i2c_rw,
            DAT_WR  => i2c_data,
            BUSY    => busy,
            SDA     => sda_internal,
            ACK_ERR => ack_err
        );
    DCM_SYS : DCM
        port map(
            SYSCLK_P => SYSCLK_P,       --take differential input clock
            SYSCLK_N => SYSCLK_N,
            SYSCLK   => sysclk,         --200 MHz system clock 
            PXLCLK   => pxlclk,         --and pixel clock
            PXLCLK2X => pxlclk2x_int    --pixel clock at double rate
        );

    Clk_Div : Clk_Divider
        generic map(
            INPUT_FREQ => 200000000,    --200 MHz system input
            OUT1_FREQ  => 100000,       --to work correctly, 200 must go into all frequencies (x2).
            OUT2_FREQ  => 200000        --i.e. from 200, cannot generate 40 as 200/40/2 = 2.5, which will be 2
        )
        port map(
            SYSCLK      => sysclk,
            RESET_N     => reset_n_input,
            RESET_N_OUT => reset_n,
            OUT1        => scl_internal,
            OUT2        => scl2x_internal
        );

    data_load : component DVI_INITIALISE
        port map(
            SYSCLK      => sysclk,
            ACK_ERR     => ack_err,
            BUSY        => busy,
            RESET_N     => reset_n,
            COUNT       => LED,
            DVI_WR      => i2c_rw,
            DVI_REGDATA => i2c_reg,
            DVI_WDATA   => i2c_data
        );

    interface : DVI_INTERFACE
        port map(
            PIXEL_CLK => pxlclk_p_int,
            RESET_N   => reset_n,
            PXL_ADDR  => open,
            HSYNC     => HSYNC,
            VSYNC     => VSYNC,
            ENABLE    => DVI_ENABLE
        );

    pxl_mux : DVI_MUX
        port map(
            PXLCLK   => pxlclk_p_int,
            PXLCLK2X => pxlclk2x_int,
            PXL_DAT  => x"FF0000",
            DATA     => DVI_DATA,
            RESET_N  => reset_n
        );
    ------------OUTPUT BUFFERS (CLOCK FORWARDING)------------
    ODDR_pxlclk_p : ODDR2
        generic map(
            DDR_ALIGNMENT => "NONE",
            INIT          => '0',
            SRTYPE        => "SYNC")
        port map(
            Q  => PXLCLK_P,             --output to positive output
            C0 => pxlclk_p_int,         --differential input
            C1 => pxlclk_n_int,
            CE => '1',                  --chip enable tied high
            D0 => '1',
            D1 => '0',
            R  => '0',
            S  => '0'
        );

    ODDR_pxlclk_n : ODDR2
        generic map(
            DDR_ALIGNMENT => "NONE",
            INIT          => '0',
            SRTYPE        => "SYNC")
        port map(
            Q  => PXLCLK_N,             --output to negative output
            C0 => pxlclk_n_int,
            C1 => pxlclk_p_int,
            CE => '1',
            D0 => '1',
            D1 => '0',
            R  => '0',
            S  => '0'
        );

    out2_bufg : BUFG port map(I => pxlclk, O => pxlclk_p_int); --ERROR THROWN ON I/O HERE 
    ----------------Mappings---------------------------
    reset_n_input <= not BTN;           --when button pressed, reset
    SCL           <= 'Z' when scl_internal = '1' else scl_internal;
    SCL_DBG       <= 'Z' when scl_internal = '1' else scl_internal;
    SDA           <= sda_internal;
    SDA_DBG       <= SDA;               --copy SDA to debug line

    DVIRESET_N   <= reset_n;            --reset DVI device 
    pxlclk_n_int <= not pxlclk_p_int;   --create differential pair

end behavior;

DVI_MUX When I added this block to the system, the error was thrown where it previously wasnt

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

ENTITY DVI_MUX IS
    PORT(
        PXLCLK   : IN  STD_LOGIC;       --pixel clock
        PXLCLK2X : IN  STD_LOGIC;       --double freq. pixel clock
        PXL_DAT  : IN  STD_LOGIC_VECTOR(23 downto 0); --pixel in RGB format
        DATA     : OUT STD_LOGIC_VECTOR(11 downto 0); --
        RESET_N  : IN  STD_LOGIC
    );
END ENTITY DVI_MUX;

architecture RTL of DVI_Mux is
begin
    mux_proc : process(PXLCLK2X)
    begin
        if falling_edge(PXLCLK2X) then
            if PXLCLK = '0' then        -- if pxlclk low, load first half of pixel
                DATA <= PXL_DAT(23 downto 16) & PXL_DAT(11 downto 8);
            else    --else load second half
                DATA <= PXL_DAT(15 downto 12) & PXL_DAT(7 downto 0);
            end if;
            if RESET_N = '0' then --if reset active7
                DATA <= (others => '1');
            end if;
        end if;
    end process;

end architecture RTL;

Solution

  • This doesn't directly answer your question, but it looks like you're trying to use a DDR output primitive to drive an external differential clock pin. This is a good thing to do, but the way you have done it seems unconventional. The standard way to do this would look more like this:

    EDIT: I've realised that I misunderstood the question, and have incorporated the differential output instantiation from @StuartVivian

    pxclk_inverted <= not pxlclk;
    
    ODDR_pxlclk_p : ODDR2
        generic map(
            DDR_ALIGNMENT => "NONE",
            INIT          => '0',
            SRTYPE        => "SYNC")
        port map(
            Q  => PXLCLK_OUT,
            C0 => pxlclk,
            C1 => pxclk_inverted,
            CE => '1',
            D0 => '1',
            D1 => '0',
            R  => '0',
            S  => '0'
        );
    
    inst_obufds : OBUFDS 
    generic map (
      IOSTANDARD=>"LVDS_25"
    ) 
    port map 
    (
      O => PXLCLK_OUT_P, 
      OB  => PXLCLK_OUT_N, 
      I   => PXLCLK_OUT
    );
    

    In IO assignment, PXLCLK_OUT_N/P will then be set to use a differential IO standard. In a graphical pin assignment window, this port will then use two pins, and will only allow you to assign them to a valid positive/negative pair. What you're trying to do is manually create the positive and negative signals before the DDR output primitive, which is not the way it's supposed to work.

    I think if you use this technique, and get rid of your BUFG, your problem should go away. If not, maybe update the question with this, and whatever the new problem might be.


    Now that you've updated the question, I can see that your pixel output data also seems to be a DDR bus. You are trying to infer a DDR output behavior by implementing a multiplexer using a clock signal as the select line.

    The more standard way to do this would be to instantiate n DDR output primitives, one for each bit of the parallel DDR output. According to the latest VHDL standard (VHDL2008) it is possible to infer a DDR output, but the problem with this is that this technique does not yet have widespread toolchain support.