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?
5 Answers
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.
-
1\$\begingroup\$ Not bad, but you can add/compare unsigned with integer, so:
if prescaler = 50_000_000/4 then ...
andprescaler <= prescaler + 1;
would be a little simpler. \$\endgroup\$– user16324Commented Mar 19, 2013 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\$– evadingCommented Sep 13, 2013 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 lineclk_2Hz <= not clk_2Hz;
. I've edited in the fix. \$\endgroup\$– stanriCommented Sep 14, 2013 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');
andprescaler <= '0';
? \$\endgroup\$– cbmeeksCommented Jan 28, 2016 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\$– cbmeeksCommented Jan 28, 2016 at 22:48
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;
-
\$\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 assignnewClk <= not newClk
? \$\endgroup\$– stanriCommented Mar 19, 2013 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\$– MLMCommented Mar 19, 2013 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\$ Commented Mar 20, 2013 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\$ Commented Mar 20, 2013 at 13:56
-
\$\begingroup\$ It is synthesizable, but basically only works on SRAM-based FPGAs, like most from Xilinx, Altera or Lattice. \$\endgroup\$ Commented Mar 20, 2013 at 18:34
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.
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;
And then you will be able to specify what frequency do you want:
-
1\$\begingroup\$ This is not a correct answer for this particular application, as Clocking Wizard cannot generate such a low frequency. \$\endgroup\$ Commented Feb 3, 2021 at 15:50
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;