0
\$\begingroup\$

TL;DR: New to Vivado Clocking Wizard, using the clock pin from the FPGA for anything other than the input to MMCM/PLL IP fails timing.

I'm working with Vivado ML 2022 in VHDL targeting an Artix-7 FPGA (Digilent Basys 3 and/or Nexys A7-100T to be specific). I recently discovered the Vivado Clocking Wizard as a method for frequency synthesis and have been trying to figure out how to use it. I'm familiar enough with component instantiation and have been able to instantiate the generated IP core and connect its signals to the rest of my hardware, as is done here. For the purposes of this question, what the rest of my hardware entails isn't exactly important (and often changes) - I can elaborate if necessary, but fundamentally I'm using the IP core to easily generate an arbitrary clock for an arbitrary hardware component, primarily to determine its minimum delay.

When I first tried out this IP core, I found that implementing it caused my timing to go negative regardless of what I did, very similar to this problem. I remember I saw duplicate clocks just like that post at some point, but I don't recall what I did to resolve that (maybe it was the decimal precision thing) and I can't seem to recreate it (nor do I want to). In any case, that part of the issue doesn't seem to be present anymore. That post also informed me about the constraints generated by the clocking wizard, which helped me identify my main problem, which I'm currently trying to figure out: if I have a create_clock constraint for the FPGA source clock (as I do with every other hardware I've used) and I try to use that clock elsewhere in my design, I encounter a clock redefinition critical warning, and the design fails to meet timing. If I don't use the create_clock constraint, I still can't feasibly use that clock in the design outside of the input to the IP core - or at least, Vivado can't analyze timing properly from that, which makes sense.

The relevant constraints are as follows:

set_property -dict { PACKAGE_PIN E3    IOSTANDARD LVCMOS33 } [get_ports { clk }]; 

# This is the problem constraint
create_clock -add -name sys_clk_pin -period 10.000 -waveform {0 5} [get_ports { clk }];

Side note: now that I think about it, perhaps there's a way to constrain the clock such that it uses the one defined by the IP core instead of trying to override it?

Here's my customized IP from the clock wizard: Customized Clock Wizard IP Core

The following is an example code that I used with the IP core to manually verify that the frequency I was generating was actually correct. My use of a buffer signal and a conditional check for the locked signal from the IP core may be excessive - if there's a better way to do this, or if it's not necessary, please let me know.

entity top is
  port (
    clk   : in    std_logic;
    reset : in    std_logic;
    leds  : out   std_logic_vector(15 downto 0)
  );
end top;

architecture rtl of top is
  signal counter : std_logic_vector(31 downto 0);

  component clk_wiz_0 is
    port (
      reset       : in    std_logic;
      clk_in1     : in    std_logic;
      passthrough : out   std_logic;
      clk_out2    : out   std_logic;
      locked      : out   std_logic
    );
  end component;

  signal div_clk     : std_logic;
  signal div_clk_buf : std_logic;
  signal mmcm_locked : std_logic;
  signal clk_pt      : std_logic;

begin

  c_div : clk_wiz_0
    port map (
      reset       => reset,
      clk_in1     => clk,
      passthrough => clk_pt,
      clk_out2    => div_clk_buf,
      locked      => mmcm_locked
    );

  -- This may be unnecessary
  div_clk <= div_clk_buf when (mmcm_locked = '1') else '0';

  leds_proc : 
  process (clk, reset) begin
    if (reset = '1') then
      leds <= (others => '0');
    elsif (clk'event and clk = '1') then
      leds <= counter(counter'left downto counter'left - 15);
    end if;
  end process;

  counter_proc : 
  process (div_clk, reset) begin
    if (reset = '1') then
      counter <= (others => '0');
    elsif (mmcm_locked = '1') then
      if (div_clk'event and div_clk = '1') then
        if (and counter) then
          counter <= (others => '0');
        else
          counter <= counter + 1;
        end if;
      end if;
    end if;
  end process;
end architecture rtl;

As you can see from the above code, I attempted to resolve this by creating a passthrough signal, clk_pt, that matches the input clock clk directly. Using clk_pt instead of clk in leds_proc passes timing. However, I don't understand why I can't simply use clk directly like that. I also found this source which implies that having logic on the input side of the MMCM is undesirable, but I don't know enough to understand why that is. Furthermore, when trying to integrate the IP core into a design but not (instantiated) at the top-level, having logic on the input side becomes unavoidable. Despite extensive searching, I wasn't able to find any other sources related to this, nor could I find any in the relevant Xilinx documentation (though, I could've completely missed something).

I did find a method of structuring components more horizontally so that the IP core could be at the top-level thanks to this post. I'm going to try and rework my hardware to this paradigm, but I still want to at least understand why what I was trying to do doesn't work. Is putting logic on the input side of an MMCM/PLL generally frowned upon? Are MMCMs or PLLs never put anywhere other than the top level of a design? Am I constraining something incorrectly? Is there another way I can "wrest control from Vivado" as this answer puts it? Is there a fundamental concept I'm completely missing?

\$\endgroup\$
2
  • \$\begingroup\$ I believe you can choose to have the input of the clock wizard be a clock capable input pin, in which case you won't have to write any clock constraints. As to why your project fails timing; my guess is because of [en.wikipedia.org/wiki/Clock_domain_crossing](Clock Domain Crossing). You are driving the counter variable with the div_clk but are reading it with clock. \$\endgroup\$
    – Schottky
    Jun 29, 2023 at 19:04
  • \$\begingroup\$ @Schottky single-ended clock capable pin is the default for the clock wizard and is what I was using. It seems the bulk of my issues were because it was generating clock constraints for me, thus adding my own constraint was duplicating the clock and creating an invalid redefinition on the clock tree. Having just one constraint (in either place) seems to work, but regardless I was still getting two warnings. I attempted to swap the mode for the IP input to global buffer, and that essentially solved my issues. Writing a cohesive answer now. \$\endgroup\$ Jul 1, 2023 at 0:11

1 Answer 1

3
\$\begingroup\$

Thanks to @Schottky's comment I was able to determine a solution. The root of the problem was mostly my lack of knowledge.

TL;DR

TL;DR, I used the default source mode for the clocking wizard, single ended clock capable pin. Changing this to global buffer resolved my issues. The following is an explanation of how I realized this.

Source: Single Ended Clock Capable Pin

First, I looked again at the constraint file generated by the clock wizard. This can be annoying to find - in the Sources panel, switch from Hierarchy to IP Sources, and then look under clk_wiz_0 (or your custom IP name), then Synthesis. The clk_wiz_0.xdc file is what you're looking for. If you have it in single ended clock capable pin mode, there will be a create_clock constraint here that matches the input clock you specified to create the IP. In my case, it also inconveniently matches the create_clock constraint I have in my primary constraint file, which triggers a critical warning for an invalid redefinition on the clock tree. It also is likely to fail timing.

Then, I tested using only one constraint or the other. This seems to work, but it also triggers two warnings of note:

TIMING #1 Critical Warning: A primary clock c_div/inst/clk_in1 is created on an inappropriate internal pin c_div/inst/clk_in1. It is not recommended to create a primary clock on a hierarchical pin when its driver pin has a fanout connected to multiple clock pins

and:

CKLD #1 Warning: Clock net clk_IBUF is directly driven by an IO rather than a Clock Buffer or may be an IO driving a mix of Clock Buffer and non-Clock loads. This connectivity should be reviewed and corrected as appropriate. Driver(s): ...

In my case, the drivers were my leds, clk_IBUF_inst.O, and c_div.clk_in1. This second warning especially gave me a clue to the solution.

Additionally, before I read the aforementioned comment, I successfully implemented the horizontal layout mentioned in my original post by using primarily the passthrough output from the MMCM. However, I had some D-flip-flops from cross component (and likely clock domain) signals. Originally, I accidentally clocked them directly with clk, which passed timing, but oddly enough, clocking them with the passthrough clock failed timing. This also yielded the same two errors, with the latter's drivers as follows: clk_IBUF_inst.O, delay_start_ff.clk, delay_done_ff.clk, c_div.clk_in1.

Source: Global Buffer

@Schottky's comment prompted me to look at the options for source clocks in the clocking wizard. I thought to try using global buffer instead, and this essentially solved my issues. I noticed two things: 1. the problematic constraints in the .xdc generated by the clock wizard were commented out automatically, and 2. an additional BUFG was placed after the IBUF following the clock port, which wasn't present with the single ended option. I was able to find out a bit more about buffers from this Xilinx support post. Also, this source also makes lot more sense to me now. Essentially, a BUFG isn't needed if the source clock pin is connected directly to the MMCM and nothing else, but it is needed if there is other logic connected directly to the source clock pin. With more knowledge of clock domain crossings and sychronization I should be able to adapt this for my larger hardware.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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