Jump to content
  • 0

Nexys4 DDR: Fix hold time violation


maximb

Question

Hi,

I am using Vivado 2016.4 to program the Nexys4 DDR 7-segment display.

I have a very simple VHDL project, which works as follows:

  • 100 MHz clock is used to increment an 8-bit counter
  • when this counter overflows, it inverts the value of a local signal called "slowclk". Hence, "slowclk" is "clk" divided by 512.
  • the "slowclk" is used to increment another 8-bit counter, the output of which is assigned to the 7-segment display segment selector pins on the board.

Complete VHDL source:

Spoiler

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.NUMERIC_STD.ALL;

 

entity segdisp is

    port (    clk    : in    std_logic;
        seg_en    : out    std_logic_vector(7 downto 0);
        seg_cs    : out    std_logic_vector(7 downto 0) );
end;

architecture top of segdisp is
    signal r_count: natural range 0 to 255 := 0;
    signal disp_next: std_logic := '0';

    signal seg_cs_int: natural range 0 to 255;

    signal clkdiv: natural range 0 to 255 := 0;
    signal slowclk: std_logic := '0';
    
    function int2slv(arg: integer; length: positive) return std_logic_vector is
    begin
        return std_logic_vector(to_unsigned(arg, length));
    end function;
begin
    seg_cs <= int2slv(seg_cs_int, seg_cs'length);
    seg_en <= not X"80";

    divider: process (clk)
    begin
        if rising_edge(clk) and clk = '1' then
            if clkdiv = 255 then
                clkdiv <= 0;
                slowclk <= not slowclk;
            else
                clkdiv <= clkdiv + 1;
            end if;
        end if;
    end process;

    main: process (slowclk)
    begin
        if rising_edge(slowclk) and slowclk = '1' then
            seg_cs_int <= seg_cs_int + 1;
        end if;
    end process;
    
end architecture top;

Note: I understand that given such division, the effect on the digit segments will still not be visible - I just want to demonstrate the timing problem.

However, the design fails to meet timing constraints as follows in attached pictures:

G35bJhG.png

k2yITaN.png

Timing constraint failures in more detail, including the full source VHDL:

G7Wd4Hb.png

Clock routing on the FPGA:

jOSAD62.png

The following is the .xdc constraints file (commented-out definitions are omitted):

Spoiler

 

## Clock signal
set_property -dict {PACKAGE_PIN E3 IOSTANDARD LVCMOS33} [get_ports { clk }]
create_clock -add -name sys_clk_pin -period 10.00 -waveform {0.000 5.000} [get_ports { clk }]

...

set_property -dict {PACKAGE_PIN T10 IOSTANDARD LVCMOS33} [get_ports {seg_cs[7]}]
set_property -dict {PACKAGE_PIN R10 IOSTANDARD LVCMOS33} [get_ports {seg_cs[6]}]
set_property -dict {PACKAGE_PIN K16 IOSTANDARD LVCMOS33} [get_ports {seg_cs[5]}]
set_property -dict {PACKAGE_PIN K13 IOSTANDARD LVCMOS33} [get_ports {seg_cs[4]}]
set_property -dict {PACKAGE_PIN P15 IOSTANDARD LVCMOS33} [get_ports {seg_cs[3]}]
set_property -dict {PACKAGE_PIN T11 IOSTANDARD LVCMOS33} [get_ports {seg_cs[2]}]
set_property -dict {PACKAGE_PIN L18 IOSTANDARD LVCMOS33} [get_ports {seg_cs[1]}]

set_property -dict {PACKAGE_PIN H15 IOSTANDARD LVCMOS33} [get_ports {seg_cs[0]}]

set_property -dict {PACKAGE_PIN J17 IOSTANDARD LVCMOS33} [get_ports {seg_en[0]}]
set_property -dict {PACKAGE_PIN J18 IOSTANDARD LVCMOS33} [get_ports {seg_en[1]}]
set_property -dict {PACKAGE_PIN T9 IOSTANDARD LVCMOS33} [get_ports {seg_en[2]}]
set_property -dict {PACKAGE_PIN J14 IOSTANDARD LVCMOS33} [get_ports {seg_en[3]}]
set_property -dict {PACKAGE_PIN P14 IOSTANDARD LVCMOS33} [get_ports {seg_en[4]}]
set_property -dict {PACKAGE_PIN T14 IOSTANDARD LVCMOS33} [get_ports {seg_en[5]}]
set_property -dict {PACKAGE_PIN K2 IOSTANDARD LVCMOS33} [get_ports {seg_en[6]}]
set_property -dict {PACKAGE_PIN U13 IOSTANDARD LVCMOS33} [get_ports {seg_en[7]}]

...

set_property BITSTREAM.GENERAL.COMPRESS TRUE [current_design]
set_property BITSTREAM.CONFIG.CONFIGRATE 33 [current_design]
set_property CONFIG_MODE SPIx4 [current_design]

set_property CFGBVS VCCO [current_design]
set_property CONFIG_VOLTAGE 3.3 [current_design]

 

From what little I know about FPGA clock routing and resources, I understand this to be due to the high-frequency clock and associated logic being in different regions to each other, thus requiring the implementation run to route the clock signal through awkward paths; as a consequence, the total signal propagation time is such, that before the logic relevant to the current clock pulse is evaluated, the next clock front is already present.

Am I correct in this thinking? And in either case, how can I fix the timing issues that Vivado warns about?

Link to comment
Share on other sites

2 answers to this question

Recommended Posts

@maximb

This type of issue is typical of a logic-gated clock. Usually you can get around it by replacing slowclk with an enable strobe and clocking all of your processes on the main fast clock signal.

Pseudocode of what I mean follows:

generate_strobe: process (clk)
begin
    if rising_edge(clk) then
        if <statement> then --however you would normally toggle slowclk froom low to high
            slowclk_strb <= 1;
        else
            slowclk_strb <= 0;
        end if;
    end if;
end process;

using_the_strobe: process (clk)
begin
    if rising_edge(clk) then
        if slowclk_strb = 1 then
            <do stuff>;
        end if;
    end if;
end process;

Hope this helps,

Arthur

Link to comment
Share on other sites

@maximb,

The logic clock issue is one of the several common digilent forum requests I've come across.

This article discusses several different approaches for demonstrating timing, different from the logic clock you've outlined above.  You might find that some of these reiterate what @artvvb commented above.

Crossing clock domains is also a very difficult issue to deal with.  This paper discusses many of the solutions, together with some of the reasons why crossing clock domains is generally a bad idea.

Dan

Link to comment
Share on other sites

Archived

This topic is now archived and is closed to further replies.

×
×
  • Create New...