VHDL: creating a very slow clock pulse based on a very fast clock
Asked Answered
M

4

9

(I'd post this in EE but it seems there are far more VHDL questions here...)

Background: I'm using the Xilinx Spartan-6LX9 FPGA with the Xilinx ISE 14.4 (webpack).

I stumbled upon the dreaded "PhysDesignRules:372 - Gated clock" warning today, and I see there's a LOT of discussion out there concerning that in general. The consensus seems to be to use one of the DCMs on the FPGA to do clock division but... my DCM doesn't appear to be capable of going from 32 MHz to 4.096 KHz (per the wizard it bottoms out at 5MHz based on 32MHz... and it seems absurd to try to chain multiple DCMs for this low-frequency purpose).

My current design uses clk_in to count up to a specified value (15265), resets that value to zero and toggles the clk_out bit (so I end up with a duty cycle of 50%, FWIW). It does the job, and I can easily use the rising edge of clk_out to drive the next stage of my design. It seems to work just fine, but... gated clock (even though it isn't in the range where clock skew would IMHO be very relevant). (Note: All clock tests are done using the rising_edge() function in processes sensitive to the given clock.)

So, my questions:

  • If we're talking about deriving a relatively slow clk_out from a much faster clk_in, is gating still considered bad? Or is this sort of "count to x and send a pulse" thing pretty typical for FPGAs to generate a "clock" in the KHz range and instead some other unnecessary side-effect may be triggering this warning instead?

  • Is there a better way to create a low KHz-range clock from a MHz-range master clock, keeping in mind that using multiple DCMs appears to be overkill here (if it's possible at all given the very low output frequency)? I realize the 50% duty cycle may be superfluous but assuming one clock in and not using the on-board DCMs how else would one perform major clock division with an FPGA?

Edit: Given the following (where CLK_MASTER is the 32 MHz input clock and CLK_SLOW is the desired slow-rate clock, and LOCAL_CLK_SLOW was a way to store the state of the clock for the whole duty-cycle thing), I learned that this configuration causes the warning:

architecture arch of clock is
    constant CLK_MASTER_FREQ: natural := 32000000; -- time := 31.25 ns
    constant CLK_SLOW_FREQ: natural := 2048;
    constant MAX_COUNT: natural := CLK_MASTER_FREQ/CLK_SLOW_FREQ;
    shared variable counter: natural := 0;
    signal LOCAL_CLK_SLOW: STD_LOGIC := '0';
begin
    clock_proc: process(CLK_MASTER)
    begin
        if rising_edge(CLK_MASTER) then
            counter := counter + 1;
            if (counter >= MAX_COUNT) then
                counter := 0;
                LOCAL_CLK_SLOW <= not LOCAL_CLK_SLOW;
                CLK_SLOW <= LOCAL_CLK_SLOW;
            end if;
        end if;
    end process;
end arch;

Whereas this configuration does NOT cause the warning:

architecture arch of clock is
    constant CLK_MASTER_FREQ: natural := 32000000; -- time := 31.25 ns
    constant CLK_SLOW_FREQ: natural := 2048;
    constant MAX_COUNT: natural := CLK_MASTER_FREQ/CLK_SLOW_FREQ;
    shared variable counter: natural := 0;
begin
    clock_proc: process(CLK_MASTER)
    begin
        if rising_edge(CLK_MASTER) then
            counter := counter + 1;
            if (counter >= MAX_COUNT) then
                counter := 0;
                CLK_SLOW <= '1';
            else
                CLK_SLOW <= '0';
            end if;
        end if;
    end process;
end arch;

So, in this case it was all for lack of an else (like I said, the 50% duty cycle was originally interesting but wasn't a requirement in the end, and the toggle of the "local" clock bit seemed quite clever at the time...) I was mostly on the right track it appears.

What's not clear to me at this point is why using a counter (which stores lots of bits) isn't causing warnings, but a stored-and-toggled output bit does cause warnings. Thoughts?

Maus answered 6/3, 2013 at 10:37 Comment(0)
S
11

If you just need a clock to drive another part of your logic in the FPGA, the easy answer is to use a clock enable.

That is, run your slow logic on the same (fast) clock as everything else, but use a slow enable for it. Example:

signal clk_enable_200kHz  : std_logic;
signal clk_enable_counter : std_logic_vector(9 downto 0);

--Create the clock enable:
process(clk_200MHz)
begin
  if(rising_edge(clk_200MHz)) then
    clk_enable_counter <= clk_enable_counter + 1;
    if(clk_enable_counter = 0) then
      clk_enable_200kHz <= '1';
    else
      clk_enable_200kHz <= '0';
    end if;
  end if;
end process;


--Slow process:
process(clk_200MHz)
begin
  if(rising_edge(clk_200MHz)) then
    if(reset = '1') then
      --Do reset
    elsif(clk_enable_200kHz = '1') then
      --Do stuff
    end if;
  end if;
end process;

The 200kHz is approximate though, but the above can be extended to basically any clock enable frequency you need. Also, it should be supported directly by the FPGA hardware in most FPGAs (it is in Xilinx parts at least).

Gated clocks are almost always a bad idea, as people often forget that they are creating new clock-domains, and thus do not take the necessary precautions when interfacing signals between these. It also uses more clock-lines inside the FPGA, so you might quickly use up all your available lines if you have a lot of gated clocks.

Clock enables have none of these drawbacks. Everything runs in the same clock domain (although at different speeds), so you can easily use the same signals without any synchronizers or similar.

Shellashellac answered 6/3, 2013 at 10:50 Comment(3)
Intriguing... that's pretty much what I was doing already, though I was using logic to toggle a local std_logic bit when it reached the required value (no "else" clause) and then assign it to an output (the 50% duty cycle thing was no longer required anyway). Taking that toggle out and then adding an else clause (the equivalent to your "clk_enable_200kHz <= '0';" above) got rid of the warning! So I had the right idea except for my attempt to get a 50% duty cycle out of it that way... thus I wonder, was it simply the fact I effectively created a flip-flop there the cause of all this?Maus
That sounds strange. Have you been using the gated clock signal in the same way in both situations? The warning should depend on a logic signal being used as a clock, not on how that logic signal is generated.Shellashellac
I've updated my question with "before and after" code (I tested both forms and only the "before" one triggers this error... though obviously it has an additional storage bit in it as you'll see). It's a little academic at this point but it'd be very nice to understand why what I did in the first place was "wrong" (all I can figure is it's got to do with either having no "else" or the extra bit being toggled or a combination of both, all other things being equal throughout.)Maus
A
3

Note for this example to work this line,

signal clk_enable_counter : std_logic_vector(9 downto 0);

must be changed to

signal clk_enable_counter : unsigned(9 downto 0);

and you'll need to include this library,

library ieee; use ieee.numeric_std.all;

Argonaut answered 20/8, 2014 at 17:49 Comment(0)
C
0

Both your samples create a signal, one of which toggles at a slow rate, and one of which pulses a narrow pulse at a "slow-rate". If both those signals go to the clock-inputs of other flipflops, I would expect warnings about clock routing being non-optimal.

I'm not sure why you get a gated clock warning, that usually comes about when you do:

gated_clock <= clock when en = '1' else '0';
Canikin answered 7/3, 2013 at 9:57 Comment(2)
I'm thinking that LOCAL_CLK_SLOW is the gated clock here... though why the counter variable doesn't have this effect is still unclear to me.Maus
But LOCAL_CLOCK_SLOW is just a signal written to synchronously, like all the other normal synchronous code... Something very weird is going on!Canikin
A
-2

Here's a Complete Sample Code :

LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.ALL;
USE IEEE.NUMERIC_STD.ALL;

ENTITY Test123 IS

    GENERIC (
        clk_1_freq_generic : unsigned(31 DOWNTO 0) := to_unsigned(0, 32); -- Presented in Hz
        clk_in1_freq_generic : unsigned(31 DOWNTO 0) := to_unsigned(0, 32) -- Presented in Hz, Also
    );

    PORT (
        clk_in1 : IN std_logic := '0';
        rst1 : IN std_logic := '0';
        en1 : IN std_logic := '0';
        clk_1 : OUT std_logic := '0'
    );

END ENTITY Test123;

ARCHITECTURE Test123_Arch OF Test123 IS
    --
    SIGNAL clk_en_en : std_logic := '0';
    SIGNAL clk_en_cntr1 : unsigned(31 DOWNTO 0) := (OTHERS => '0');
    --
    SIGNAL clk_1_buffer : std_logic := '0';
    SIGNAL clk_1_freq : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Hz, Also
    SIGNAL clk_in1_freq : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Hz
    --
    SIGNAL clk_prescaler1 : unsigned(31 DOWNTO 0) := (OTHERS => '0'); -- Presented in Cycles (Relative To The Input Clk.)
    SIGNAL clk_prescaler1_halved : unsigned(31 DOWNTO 0) := (OTHERS => '0');
    --

BEGIN
    clk_en_gen : PROCESS (clk_in1)
    BEGIN
        IF (clk_en_en = '1') THEN

            IF (rising_edge(clk_in1)) THEN
                clk_en_cntr1 <= clk_en_cntr1 + 1;

                IF ((clk_en_cntr1 + 1) = clk_prescaler1_halved) THEN   -- a Register's (F/F) Output Only Updates Upon a Clock-Edge : That's Why This Comparison Is Done This Way !

                    clk_1_buffer <= NOT clk_1_buffer;
                    clk_1 <= clk_1_buffer;
                    clk_en_cntr1 <= (OTHERS => '0');

                END IF;

            END IF;

        ELSIF (clk_en_en = '0') THEN

            clk_1_buffer <= '0';
            clk_1 <= clk_1_buffer;
            clk_en_cntr1 <= (OTHERS => '0'); -- Clear Counter 'clk_en_cntr1'

        END IF;

    END PROCESS;

    update_clk_prescalers : PROCESS (clk_in1_freq, clk_1_freq)
    BEGIN
        clk_prescaler1 <= (OTHERS => '0');
        clk_prescaler1_halved <= (OTHERS => '0');
        clk_en_en <= '0';

        IF ((clk_in1_freq > 0) AND (clk_1_freq > 0)) THEN

            clk_prescaler1 <= (clk_in1_freq / clk_1_freq); -- a Register's (F/F) Output Only Updates Upon a Clock-Edge : That's Why This Assignment Is Done This Way !
            clk_prescaler1_halved <= ((clk_in1_freq / clk_1_freq) / 2); -- (Same Thing Here)

            IF (((clk_in1_freq / clk_1_freq) / 2) > 0) THEN -- (Same Thing Here, Too)
                clk_en_en <= '1';
            END IF;

        ELSE
            NULL;
        END IF;

    END PROCESS;

    clk_1_freq <= clk_1_freq_generic;
    clk_in1_freq <= clk_in1_freq_generic;

END ARCHITECTURE Test123_Arch;
Allowed answered 4/7, 2017 at 0:21 Comment(11)
You dig up a four year old question that already has an accepted answer, and answer it by posting a bulk of not-properly-indented code. It is not even a complete minimal verifiable example. Please don't do thisEcclesiastical
@JHBonarius: Well, The Code In That 'Accepted Answer' Wasn't Working For Me & My "Bulk Of Code" Is a Mere ~40 LOC Long Anyhow.Allowed
I'm sorry, but your code has no explanation or comments. And for instance look at the process proc1_running_at_clk_1: It's empty... And why are clk_in1_freq and clk_1_freq signals, when it looks like they should be constants.... and not even 64 bit unsigneds, but just integer. Looking further into you code, it just doesn't make any sense.Ecclesiastical
I Do Believe That My Code Makes Sense & That It's Highly Usable. As Per Process proc1_running_at_clk_1 - It's Obviously a Mere Placeholder. As Per clk_in1_freq & clk_1_freq - One Who Wish To Turn These Into Constants, Can Do So Extremely Easily, It's Just That In So Doing - The Possibility For Run-Time Adjustments Would Be Lost.Allowed
How do you suppose clk_prescaler1 <= clk_in1_freq / clk_1_freq would work in an FPGA if the values aren't constants... And why are you typing every word with a capital?Ecclesiastical
Well, Singal clk_en_en, Along With Process update_clk_prescalers, Are Intended To Make a Way To Dynamically Adjust The Generated Clock At Run-Time.Allowed
Would You Clarify Your Question ?Allowed
I have one question: Are you aware that it is grammatically incorrect to capitalize every word in english?Ecclesiastical
And then my comment: You are suggesting your non-commented non-described design to offer a variable clock rate, which you could achieve by changing clk_in1_freq and/or clk_1_freq, each being a 64-bit unsigned. You then put some line containing clk_prescaler1 <= clk_in1_freq / clk_1_freq that would calculate the division factor. So just suggested to implemented a 64-bit division in vhdl in a combinatorial process... An FPGA has no hard-wired dividers. How would you imagine this being generated? If you don't believe me: make clk_in1_freq and clk_1_freq ports instead of signals.Ecclesiastical
I see you've been editing your code... its now a 32 bit division instead of 64 bit. Doesn't solve the issue though.Ecclesiastical
I Think It's Time To Stop Feedin' You, Like Now.Allowed

© 2022 - 2024 — McMap. All rights reserved.