As I'm sure everyone here knows, in FPGA/CPLD design one often needs to synchronize a slower asynchronous signal (say, the SCK line of SPI) with a much faster clock signal thats directly fed to the FPGA/CPLD. My question is, how much faster does the FPGA/CPLD clock needs to be relative to my asynchronous signal? Ten times? Twenty times?
In my case less than 10x doesn't work well. Specifically: I set my SCK speed to 4 MHz whereas my clock was 20 MHz. This didn't work at all. 2 Mhz works, but occasionally I get some problems. At 1 MHz, it works very well - no issues so far.
VHDL Code for the CPLD:
library ieee; use ieee.std_logic_1164.all; entity PISO is port(CLK, nCS, SCK, nRESET : in std_logic; PI : in std_logic_vector(71 downto 0); SO : out std_logic); end PISO; architecture archi of PISO is signal tmp: std_logic_vector(PI'high downto PI'low); signal bitOut: std_logic; signal rise, fall : std_logic; signal oscena: std_logic; signal iCLK : std_logic; signal SCK_rising, SCK_falling, SCK_sync, SCK_delay : std_logic; signal CS_rising, CS_falling, CS_sync, CS_delay : std_logic; component sync generic( RESET_STATE : std_logic := '0' -- '0' for active low sync ); port( clk : in std_logic; rstN : in std_logic; d : in std_logic; q : out std_logic ); end component; begin sync1 : sync generic map( RESET_STATE => '0' ) port map( clk => clk, rstN => nRESET, d => sck, q => SCK_sync ); sync2 : sync generic map( RESET_STATE => '1' ) port map( clk => clk, rstN => nRESET, d => nCS, q => CS_sync ); process(clk, nRESET) begin if (nRESET = '0') then sck_rising <= '0'; sck_falling <= '0'; sck_delay <= '0'; elsif rising_edge(clk) then if cs_sync = '1' then sck_delay <= '0'; sck_rising <= '0'; sck_falling <= '0'; else sck_delay <= sck_sync; sck_rising <= sck_sync and (not sck_delay); sck_falling <= (not sck_sync) and sck_delay; end if; end if; end process; process(clk, nRESET) begin if (nRESET = '0') then cs_rising <= '0'; cs_falling <= '0'; cs_delay <= '0'; elsif rising_edge(clk) then cs_delay <= cs_sync; cs_rising <= cs_sync and (not cs_delay); cs_falling <= (not cs_sync) and cs_delay; end if; end process; process(CLK, nRESET) begin if (nRESET = '0') then tmp <= (others => '0'); elsif rising_edge(CLK) then if CS_sync = '0' then if SCK_falling = '1' then tmp <= tmp(PI'high -1 downto PI'low) & '0'; end if; elsif CS_sync = '1' then tmp <= PI; end if; end if; end process; SO <= tmp(PI'high) when nCS = '0' else 'Z'; end archi;
And here's the code for the sync component:
library ieee; use ieee.std_logic_1164.all; entity sync is generic ( RESET_STATE : std_logic := '0' -- '0' for active low sync ); port ( clk : in std_logic; rstN : in std_logic; d : in std_logic; q : out std_logic ); end entity; architecture behavioral of sync is signal d_meta : std_logic; begin process(clk, rstN) begin if (rstN = '0') then d_meta <= RESET_STATE; q <= RESET_STATE; elsif (clk'event and clk = '1') then d_meta <= d; q <= d_meta; end if; end process; end architecture;
Regarding simplicity, I know SPI is super simple but I'm a newbie so all of this is rather difficult for me. Only after weeks did it make sense to me that I do need to sync. the signals in the CPLD/FPGA (initially I was just using the SCK as my clock and didn't even have a separate clock on my board. It worked fine for slower speeds but increasing the speed to even 1 MHz made the naiveness of my approach obvious). I'm sure (infact, I know because of your excellent posts around here) your approach is much simpler and more elegant, the issue is that I'll need to get my head around it first because as of now it just sounds like greek to me!