# Checking the signal frequency in FPGA

in a VHDL project where the master clock is 50 MHz, how to check if a signal is 10 MHz or 20 MHz?

I have an external device where the default is 10 MHz. I am adjusting its settings via serial port and I would like to verify that the clock has changed to 20 MHz, which I could then report in status to the processor.

It comes to my mind to have two processes with counters, one for the main clock, the other for this verified signal. When the counter running with the main clock adds up to e.g. 10 then I would check the value of the second counter. If it is 2 then I have 10 MHz, and if 4 then I have 20 MHz. Is this a good approach? Are there any simpler ways?

This is a fine approach in principle, but it makes an assumption that the primary 50 MHz clock is aligned to the 10/20 MHz clocks with a known phase and skew. Your approach would be theoretically feasible under that assumption, but it also might require careful timing constraints.

If the clocks were simply unrelated, then you would have no recourse and could end up with spurious values of 1/3/5 depending on the relationship between the clocks, with the sampling period you propose. The case of 3 pulses is the hardest to resolve since it could plausibly be caused by either of the clock frequencies.

Instead, consider counting more edges - e.g. run the 50 MHz counter for at least 10 edges of the 10/20 MHz (slow) clock. Make sure to include at least one or two stages of flip-flops on the slow clock path to reduce metastability risks that can occur with unaligned signals and to ensure that the same registered signal fans out to all of the logic cells that make up that counter.

I had to put a frequency meter into an FPGA and used the following method, which you alluded to in your question...

You have a logic clock, CLK, and a clock to be measured, CLK_MEAS.

• Reduce CLK_MEAS using a divider counter. This is the only logic not clocked by CLK.
• Bring the divider's MSB into the CLK domain
• Detect MSB rising edges.
• Count the number of CLKs between MSB edges.

Below is some example VHDL of the sort of thing I did. Mine had to measure 40..430 MHz. The example uses a 4-bit divider and 8-bit result but just to illustrate the idea. You can choose whatever bit-sizes are necessary.

  ------------------------------------------------------------------------------
-- pResetMEAS. Produce the CLK_MEAS domain reset.
------------------------------------------------------------------------------
pFMEASReset : process(RST, CLK_MEAS) is
begin
if (RST = '1') then
rstMeasSR  <=  "111";

elsif rising_edge(CLK_MEAS) then
rstMeasSR  <=  rstMeasSR( 1 downto  0) & '0';

end if;
end process pFMEASReset;

rstMeas  <=  rstMeasSR(2);

------------------------------------------------------------------------------
-- pPrescaler. Divide the CLK_MEAS frequency down by 16.
------------------------------------------------------------------------------
pPrescaler : process(rstMeas, CLK_MEAS) is
begin
if (rstMeas = '1') then
clkMeasDivCtr  <=  "0000";

elsif rising_edge(CLK_MEAS) then
clkMeasDivCtr  <=  clkMeasDivCtr + "0001";

end if;
end process pPrescaler;

clkMeasDivCtrMSB   <=  clkMeasDivCtr(3);

------------------------------------------------------------------------------
-- pFreqCounter. Measure CLKs between divided FMEAS rising edges.
------------------------------------------------------------------------------
pFreqCounter : process(RST, CLK) is
begin
if (RST = '1') then
divCtrMSBSR      <=  "01010";
divCtrMSBRise    <=  '0';
freqCtr          <=  X"00";
freqCount        <=  freqCtr;
freqCountValid   <=  divCtrMSBRise;

elsif rising_edge(CLK) then

--------------------------------------------------------------------------
-- Bring the divider counter's MSB into the CLK domain.
--------------------------------------------------------------------------
divCtrMSBSR      <=  divCtrMSBSR( 3 downto  0) & clkMeasDivCtrMSB;

if (divCtrMSBSR( 4 downto  1) = "0011") then
divCtrMSBRise  <=  '1';
else
divCtrMSBRise  <=  '0';
end if;

--------------------------------------------------------------------------
-- Count CLKs between the divider MSB's rising edges, stopping at 255.
--------------------------------------------------------------------------
if (divCtrMSBRise = '1') then
freqCtr        <=  X"00";
freqCount      <=  freqCtr;

elsif (freqCtr = X"FF") then
freqCount      <=  freqCtr;

else
freqCtr        <=  freqCtr + X"01";

end if;

-- Indicate when a new result is available.
freqCountValid   <=  divCtrMSBRise;

end if;
end process pFreqCounter;