12
\$\begingroup\$

I have a Xilinx FPGA board, with a 50MHz crystal. I need to divide that down to 2Hz in VHDL. How do I do this?

\$\endgroup\$
  • 13
    \$\begingroup\$ So what have you actually tried? \$\endgroup\$ – Matt Young Mar 19 '13 at 1:10
  • \$\begingroup\$ Why not use Xilinx clock manager IP? \$\endgroup\$ – Arturs Vancans Mar 20 '13 at 13:47
18
\$\begingroup\$

Basically, there are two ways of doing this. The first is to use the Xilinx native clock synthesizer core. One of the advantages of this is that the Xlinx tools will recognise the clock as such and route it through the required pathways. The tools will also handle any timing constraints (not really applicable in this case, since it's a 2Hz clock)

The second way is to use a counter to count the number of faster clock pulses until half of your slower clock period has passed. For example, for your case, the number of fast clock pulses that make up one clock period of a slow clock cycle is 50000000/2 = 25000000. Since we want half a clock period, that's 25000000/2 = 12500000 for each half-cycle. (the duration of each high or low).

Here's what it looks like in VHDL:

library IEEE;
use IEEE.STD_LOGIC_1164.all;

-- Uncomment the following library declaration if using
-- arithmetic functions with Signed or Unsigned values
use IEEE.NUMERIC_STD.all;

entity scale_clock is
  port (
    clk_50Mhz : in  std_logic;
    rst       : in  std_logic;
    clk_2Hz   : out std_logic);
end scale_clock;

architecture Behavioral of scale_clock is

  signal prescaler : unsigned(23 downto 0);
  signal clk_2Hz_i : std_logic;
begin

  gen_clk : process (clk_50Mhz, rst)
  begin  -- process gen_clk
    if rst = '1' then
      clk_2Hz_i   <= '0';
      prescaler   <= (others => '0');
    elsif rising_edge(clk_50Mhz) then   -- rising clock edge
      if prescaler = X"BEBC20" then     -- 12 500 000 in hex
        prescaler   <= (others => '0');
        clk_2Hz_i   <= not clk_2Hz_i;
      else
        prescaler <= prescaler + "1";
      end if;
    end if;
  end process gen_clk;

clk_2Hz <= clk_2Hz_i;

end Behavioral;

Things to note:

  • The generated clock is zero during reset. This is ok for some applications, and not for others, it just depends what you need the clock for.
  • The generated clock is going to be routed as a normal signal by the Xilinx synthesis tools.
  • 2Hz is very slow. Simulating for a second is going to take a while. It's a small amount of code, so it should be relatively quick to simulate even for 1 second, but if you start adding code, the time taken to simulate a clock cycle of 2 Hz could be significantly long.

EDIT: clk_2Hz_i is used to buffer the output signal. VHDL doesn't like to use a signal on the right of an assignment when it is also an output.

\$\endgroup\$
  • 1
    \$\begingroup\$ Not bad, but you can add/compare unsigned with integer, so: if prescaler = 50_000_000/4 then ... and prescaler <= prescaler + 1; would be a little simpler. \$\endgroup\$ – Brian Drummond Mar 19 '13 at 13:48
  • \$\begingroup\$ @StaceyAnne Trying this, I get "Cannot read from 'out' object clk_o ; use 'buffer' or 'inout'" did I miss something? \$\endgroup\$ – evading Sep 13 '13 at 20:42
  • \$\begingroup\$ @evading, a buffer on the output is needed. VHDL doesn't like the fact that clk_2Hz is an output, but yet its value is being read in this line clk_2Hz <= not clk_2Hz;. I've edited in the fix. \$\endgroup\$ – stanri Sep 14 '13 at 2:17
  • \$\begingroup\$ +1 Great example. But here is where my ignorance shows (new to VHDL). What is the difference between prescaler <= (others => '0'); and prescaler <= '0';? \$\endgroup\$ – cbmeeks Jan 28 '16 at 22:18
  • \$\begingroup\$ NVM! I totally missed what others was used for when reading a VHDL book I have. It's just a shortcut for declaring all the "other" bits to a common value instead of using something like "000000000000000000....", etc. \$\endgroup\$ – cbmeeks Jan 28 '16 at 22:48
9
\$\begingroup\$

Use a clock prescaler.

Your prescaler value will be your (clock_speed/desired_clock_speed)/2 so (50Mhz(50,000,000)/2hz(2))/2 = 12,500,000 which in binary would be 101111101011110000100000.

More simply: (50,000,000)/2)/2 = 12,500,000 convert to binary -> 101111101011110000100000

Here is some code of what to do: Use newClock for whatever you need 2hz for...

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;
use IEEE.STD_LOGIC_UNSIGNED.ALL;

entity ClockPrescaler is
    port(
        clock   : in STD_LOGIC; -- 50 Mhz
        Led     : out STD_LOGIC
    );
end ClockPrescaler;

architecture Behavioral of ClockPrescaler is
    -- prescaler should be (clock_speed/desired_clock_speed)/2 because you want a rising edge every period
    signal prescaler: STD_LOGIC_VECTOR(23 downto 0) := "101111101011110000100000"; -- 12,500,000 in binary
    signal prescaler_counter: STD_LOGIC_VECTOR(23 downto 0) := (others => '0');
    signal newClock : std_logic := '0';
begin

    Led <= newClock;

    countClock: process(clock, newClock)
    begin
        if rising_edge(clock) then
            prescaler_counter <= prescaler_counter + 1;
            if(prescaler_counter > prescaler) then
                -- Iterate
                newClock <= not newClock;

                prescaler_counter <= (others => '0');
            end if;
        end if;
    end process;


end Behavioral;
\$\endgroup\$
  • \$\begingroup\$ It seems like you're generating two clocks, one of 0.5 Hz and one of 1 Hz? (since your clock period is your prescaler * 2?). Also, the "+" will give an error, since you're adding slvs, and I'm not so sure about using the overflow property of the add in this way in any case. why not just go newClock : std_logic := '0', count up to prescaler/2 and assign newClk <= not newClk? \$\endgroup\$ – stanri Mar 19 '13 at 5:31
  • \$\begingroup\$ Thanks, my logic was a bit off. I updated my initial post with some tested code now and a few of your suggestions :) \$\endgroup\$ – MLM Mar 19 '13 at 22:16
  • \$\begingroup\$ Ugh - all those ones and zeros and a comment to say what it really is! Why not use the compiler to do that for you??? And why not just use integers anyway? \$\endgroup\$ – Martin Thompson Mar 20 '13 at 11:59
  • \$\begingroup\$ I may be wrong, but I think using default values when defining signals in arhitecture as in ":= (others => '0')" is not synthesizable. \$\endgroup\$ – Arturs Vancans Mar 20 '13 at 13:56
  • \$\begingroup\$ It is synthesizable, but basically only works on SRAM-based FPGAs, like most from Xilinx, Altera or Lattice. \$\endgroup\$ – Yann Vernier Mar 20 '13 at 18:34
7
\$\begingroup\$

You usually don't actually want to clock anything that slow, just create an enable at the correct rate and use that in the logic:

 if rising_edge(50MHz_clk) and enable = '1' then

you can create the enable thus:

process 
   variable count : natural;
begin
   if rising_edge(50MHz_clk) then
       enable <= '0';
       count := count + 1;
       if count = clock_freq/desired_freq then
          enable <= '1';
          count := 0;
       end if;
    end if;
end process;

create a couple of constants with your clock frequency and desired enable frequency and away you go, with self-documenting code to boot.

\$\endgroup\$
3
\$\begingroup\$

I would rather suggest using Xilinx primitice digital clock manager IP.

It has graphical settings interface where you can specify what frequency you want. It will generate a component with your desired output as frequency.

It can be found in IP Wizard;

enter image description here

And then you will be able to specify what frequency do you want: enter image description here

\$\endgroup\$
0
\$\begingroup\$

Factor = input-signal-frecuency/output-prescaler-frecuency.

CE = Clock Enable. It should be a one clock (clk) wide pulse or high if not used.

Q = Output signal of one clock wide pulse with the desired frecuency.

library IEEE;
use IEEE.STD_LOGIC_1164.all;
use IEEE.STD_LOGIC_ARITH.all;
use IEEE.STD_LOGIC_UNSIGNED.all;

entity prescaler is

  generic (
    FACTOR : integer);

  port (
    clk : in  std_logic;
    rst : in  std_logic;
    CE  : in  std_logic;
    Q   : out std_logic);

end prescaler;

architecture for_prescaler of prescaler is
  signal counter_reg, counter_next : integer range 0 to FACTOR-1;
  signal Q_next: std_logic;
begin  -- for_prescaler

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- asynchronous reset (active low)
      counter_reg <= 0;
    elsif clk'event and clk = '1' then  -- rising clock edge
      counter_reg <= counter_next;
    end if;
  end process;

  process (counter_reg, CE)
  begin  -- process
    Q_next <= '0';
     counter_next <= counter_reg;
     if CE = '1' then
        if counter_reg = FACTOR-1  then
          counter_next <= 0;
           Q_next <= '1';
         else
           counter_next <= counter_reg + 1;
        end if;
      end if;
  end process;

  process (clk, rst)
  begin  -- process
    if rst = '1' then                   -- asynchronous reset (active low)
      Q <= '0';
    elsif clk'event and clk = '1' then  -- rising clock edge
      Q <= Q_next;
    end if;
  end process; 

end for_prescaler;
\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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