7

The FPGA project I am working on requires events within an FPGA to be triggered off a 1Hz PPS coming from a GPS module. I have sampled this pps and then tried implemented logic triggered by this sampled pps in the two processes below.

This SO post

https://stackoverflow.com/questions/37035461/is-the-use-of-rising-edge-on-non-clock-signal-bad-practice-are-there-alternativ/37036177#37036177

where a respected member commented on a post saying its not good practice to use rising_edge on a clock as slow as 1Hz(non-clock signal).

library IEEE;
use IEEE.STD_LOGIC_1164.ALL;

entity gen is port(
            sys_clk_i           : in std_logic;
            sys_rst_i           : in std_logic;
            gps_pps_i           : in std_logic;
            pps_roll_o          : std_logic_vector(5 downto 0) );
end gen;

architecture Behavioral of gen is

  signal pps_roll_s : std_logic_vector(5 downto 0);
  signal gps_pps_reg_s  : std_logic;

  PPS_SYNC_PROCESS : process(sys_clk_i, sys_rst_i)
  begin
    if rising_edge(sys_clk_i) then
      if (sys_rst_i = '1') then
        gps_pps_reg_s <= '0';
      else
        gps_pps_reg_s <= gps_pps_i;
      end if;
    end if;
  end process PPS_SYNC_PROCESS;

  PPS_ROLL_PROCESS : process(gps_pps_reg_s, sys_clk_i, sys_rst_i)
  begin
    if (sys_rst_i = '1') then
      pps_roll_s <= "000001";
    elsif (gps_pps_reg_s'event and gps_pps_reg_s='1') then
      --elsif (rising_edge(gps_pps_reg_s)) then                                                                                                                                                                                              
      pps_roll_s <= pps_roll_s rol 1;
    end if;   
  end process PPS_ROLL_PROCESS;

  pps_roll_o <= pps_roll_s;

  end Behavioral;

I am unable to use

if rising_edge(sys_clk_i) then
  if rising_edge(gpd_pps_reg_s) then
    pps_roll_s <= "000001";

because Vivado does not allow nesting

if rising_edge

I can't think of any other way to achieve this unless I can use rising_edge on a sampled 1Hz clock?

zoulzubazz
  • 71
  • 3

3 Answers3

6

Use the fast clock to sample the slow clock. Double (or triple) buffer and evaluate buffer levels.

Something along the lines of:

process (fast_clk, reset)
begin 
    if reset = '1' then 
        slow_clk_buf1 <= '0';
        slow_clk_buf2 <= '0';
    elsif rising_edge(fast_clk) then 
        slow_clk_buf1 <= slow_clk;
        slow_clk_buf2 <= slow_clk_buf1;

        if slow_clk_buf1 = '1' and slow_clk_buf2 = '0' then
             --do stuff
        end if;
    end if;
end process; 

Assuming the PPS pulse width is longer than the fast clock period and you have already buffered the PPS somewhere, otherwise triple buffer and evaluate the last two.

Max
  • 259
  • 2
  • 12
  • I recommend using 3 FFs instead of 2 FFs and test for rising edge with the 2 lasts out of 3 FFs –  Oct 17 '22 at 20:11
  • Can you clarify why this is your recommendation? – po.pe Oct 18 '22 at 08:41
  • 3
    Because the first FF has a risk of being metastable and could trigger the LUT implemented by the if. The output of the second FF is safer (metastability filtered) and can be connected to LUT which is the same than using it in an if else end if implementing some combinatorial –  Oct 18 '22 at 15:09
4

Taking it that the 1 PPS signal on GPS_PPS_I may be in a different clock domain (not synchronous to the logic CLK), you can use the below circuit.

This operates from logic clock CLK and active-high reset RST.

  pCount1PPS : process(RST, CLK) is
  begin
    if (RST = '1') then
      gpsPPSSR    <=  "00000";
      ppsRollO    <=  "000000";

    elsif rising_edge(CLK) then

      -- Bring GPS_PPS_I into the CLK domain using a filter shift register.
      gpsPPSSR    <=  gpsPPSSR( 3 downto  0) & GPS_PPS_I;

      -- A rising edge increments the counter with rollover.
      if (gpsPPSSR( 4 downto  1) = "0011") then
        ppsRollO  <=  ppsRollO + "000001";
      end if;

    end if;
  end process pCount1PPS;

  -- Drive the output port.
  PPS_ROLL_O  <=  ppsRollO;

The domain crossing and edge detection uses shift register gpsPPSSR. The first stage of this, DFF gpsPPSSR(0), may go metastable so its level is not examined by the circuit. It's just shifted into DFF gpsPPSSR(1) a CLK later to resolve the metastability. Only bits 4:1 are used, to detect a rising edge pattern.

The 4-bit "0011" is detected, rather than the 2-bit "01", for a simple level of improved noise rejection. However, if GPS_PPS_I comes from a noisier source, it should be filtered further before use.

This circuit shows an example of that, requiring GPS_PPS_I to be sampled HIGH for LOW for 3 successive CLKs before being accepted as a '1' or '0'. But the actual level of filtering you need must first be understood then a circuit designed to meet it.

  pFilter1PPSInput : process(RST, CLK) is
  begin
    if (RST = '1') then
      gpsPPSSR        <=  "0000";
      filtGPSPPS      <=  '0';
      filtGPSPPSOld1  <=  '0';

    elsif rising_edge(CLK) then

      -- Bring GPS_PPS_I into the CLK domain using a filter shift register.
      gpsPPSSR        <=  gpsPPSSR( 2 downto  0) & GPS_PPS_I;

      -- A rising edge increments the counter with rollover.
      if (gpsPPSSR( 3 downto  1) = "000" or gpsPPSSR( 3 downto  1) = "111") then
        filtGPSPPS    <=  gpsPPSSR(3);
      end if;

      -- Keep the previous level for edge detection.
      filtGPSPPSOld1  <=  filtGPSPPS;

    end if;
  end process pFilter1PPSInput;

  -- Indicate when a rising edge has been detected.
  filtGPSPPSRise  <=  '1'  when (filtGPSPPS = '1' and filtGPSPPSOld1 = '0') else  '0';


  pCount1PPS : process(RST, CLK) is
  begin
    if (RST = '1') then
      ppsRollO    <=  "000000";

    elsif rising_edge(CLK) then

      -- A rising edge increments the counter with rollover.
      if (filtGPSPPSRise = '1') then
        ppsRollO  <=  ppsRollO + "000001";
      end if;

    end if;
  end process pCount1PPS;

  -- Drive the output port.
  PPS_ROLL_O  <=  ppsRollO;

On your comment "I am unable to use if rising_edge(sys_clk_i) then if rising_edge(gpd_pps_reg_s) then because Vivado does not allow nesting"...

Remember that you're designing using a Hardware Descriptor Language, you're not writing a computer program. In its basic form, your VHDL must describe (imply) a digital logic circuit that can be made out of latches, flip-flops and combinatorial logic (AND, OR, XOR, NOT gates).

So it's not so much that Vivado "won't allow it", it's that a logic circuit doesn't exist in the target device with two positive-edge triggered inputs. Nor would one make sense.

It's easy to confuse writing in VHDL and other HDLs with writing a computer program. A computer program consists of a series of instructions that are executed by a CPU. That's completely different to an HDL. An HDL, to give you a picture in your head, is closer to a super-enhanced kind of netlist (the output of a schematic capture tool).

TonyM
  • 21,742
  • 4
  • 39
  • 62
3

its not good practice to use rising_edge on a clock as slow as 1Hz.

It would be nice if you can find it so we could understand the reasoning.

I am unable to use "(if rising_edge(sys_clk_i) then if rising_edge(gpd_pps_reg_s)"

You should not because the synthesis would not be able to configure the hardware that fits this description (a circuit which reacts to 2 edges when they happen simultaneously).

It seems the simple solution would be to use a (much? we don't know the requirements) faster clock that evaluates the GPS 1Hz input and detects when it switches from 0 to 1.

devnull
  • 8,447
  • 2
  • 15
  • 38
  • 1
    Added the link now. Isn't this what @Max 's solution is doing? – zoulzubazz Oct 17 '22 at 13:44
  • 1
    Thanks. The problem explained there is using several signals and/or edge polarities to trigger different parts of the logic, and the problems this brings to the synthesis tool. Yes, Max suggested a kind of edge detection FSM, based on the main clock (and has just got my up-vote). You may add more FFs for synchronization (as suggested). – devnull Oct 17 '22 at 16:42