7
\$\begingroup\$

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!

\$\endgroup\$
5
  • \$\begingroup\$ What's the SCK's pulse width? \$\endgroup\$
    – stevenvh
    Commented May 30, 2012 at 13:33
  • \$\begingroup\$ @stevenvh By pulse width, I assume you mean the length of time where the SCK is high? I believe that's half of the time period, so about 0.25 uS if my math is right. I'll measure it tomorrow at work and update here. \$\endgroup\$
    – Saad
    Commented May 30, 2012 at 13:40
  • \$\begingroup\$ The reason I'm asking is that clock frequency isn't everything. If your clock is 1MHz that's 1us period. So sampling at 10MHz would give you 10 samples over that, ideally 5 high and 5 low. If your clock produces only narrow pulses, say 100ns, that may be perfectly within specs (probably, since you're allowed to clock it much faster), but you'll only have 1 sample at 10MHz. So there's a chance of missing that one. \$\endgroup\$
    – stevenvh
    Commented May 30, 2012 at 13:44
  • \$\begingroup\$ I'm not sure what's naive about using SCK as a clock inside the CPLD, unless it's something CPLD-specific. FPGAs can have multiple clock domains inside. Was the clock trace suffering from transmission-line reflections, or was it too slow, or something else? \$\endgroup\$
    – ajs410
    Commented May 30, 2012 at 20:37
  • \$\begingroup\$ @ajs410 I was told that async. design is a Bad Thing in the FPGA/CPLD world and it's better if I sycned my signals with an external clock. \$\endgroup\$
    – Saad
    Commented May 30, 2012 at 21:02

3 Answers 3

9
\$\begingroup\$

There are so many things in this question that it is difficult to know where to start.

I am assuming that your FPGA logic is a SPI slave, not a master. If it is a master then you have a whole different set of issues which I'm going to avoid going into right now.

The simple direct answer to your question is that you need to sample an async signal at least two times the frequency of your signal. So if you have a 4 MHz clock then you need to sample it at 8 MHz or higher. Of course, nothing is simple or direct in this case.

You have things a little more difficult because you are not sampling one async signal, you are sampling three (CLK, CS, and MOSI). You also need to keep those three signals time-aligned with each other through the sampling process. And you have to spit out MISO in such a way as to not violate your setup/hold time at the master.

None of this is easy, but having a higher speed clock will make things much easier. How much higher depends on your code, and you didn't post your code. I think that I could write code to do it with an 8x clock, but that is just a guess. Honestly, however, I think this is the wrong approach.

SPI is a super simple interface, and it would be good if you kept it super simple. SPI has its own clock, and if you use it as a clock then everything becomes almost easy. Instead of changing clock domains on the serial SPI interface, change clock domains on the parallel data going in/out of your shift registers. If you look at those signals carefully you might even realize that you don't need to do anything special, or if you do then it's just a flip-flop per signal. Then you don't need to have your main clock be higher than your SPI clock. Your main clock could actually be slower!

I do this on my SPI FPGA/CPLD interfaces and I have no problems running SPI at 30+ MHz, with or without a second clock domain.

\$\endgroup\$
9
  • \$\begingroup\$ "at least two times the frequency of your signal". You know that this is not quite correct. I mention in comment a 100ns pulse width at a 1MHz clock. Sampling at 2MHz will likely miss the pulse. You need twice the harmonic required to have a decent signal, amplitude-wise, and edge-wise. \$\endgroup\$
    – stevenvh
    Commented May 30, 2012 at 13:56
  • \$\begingroup\$ @stevenvh True, but I wasn't going to worry about the details too much because I went on to say that for this application you probably need 8x, or better yet do something else entirely. \$\endgroup\$
    – user3624
    Commented May 30, 2012 at 14:21
  • \$\begingroup\$ Or use hardware that will hold the pulse and has to be actively reset so you receive it? \$\endgroup\$
    – Kortuk
    Commented May 30, 2012 at 14:25
  • \$\begingroup\$ @Kortuk - That sounded like a good idea, but on second though, what if it clocks on the falling edge? That will have to be accurate. IIRC SPI clocks in and out at opposite edges. \$\endgroup\$
    – stevenvh
    Commented May 30, 2012 at 14:58
  • \$\begingroup\$ @stevenvh, or operate in opposite direction(reset sends line high, or just invert the signal for all it matters). I am just saying if there is a certain signal you are looking for something like this can help. \$\endgroup\$
    – Kortuk
    Commented May 30, 2012 at 14:59
0
\$\begingroup\$

I would suggest that, if practical, an SPI slave should have registers that are clocked from the SPI pins. Those registers, or signals that derive from them, should then be double-synchronized with other logic. Depending upon application requirements, signals from the other logic may either be double synchronized to the SPI clock or may be fed to the SPI's data output asynchronously (if the SPI data is going to be fed to a single data path which contains at least two latches before it splits, and if the application won't care whether an attempt to read something at the moment it changes yields high or low, async reporting may be just fine).

Even if one doesn't want to design an entire SPI slave interface to be SPI-clocked, one can still have a few key aspects of it clocked from the SPI wires. At minimum, I would suggest that the SPI clock wire should trigger a latch for the data and a toggle latch. Pass the data latch through one more level of synchronization than the toggle latch, and then assume that any time the toggle latch value differs from the previous one, new data exists on the clock wire. Some extra latching logic on the chip-select wire would allow reliable detection of short unselect/reselect events.

PS--Depending upon how the logic and software protocol are designed, it may be possible to have an SPI slave device work reliably even if the attached logic likes to sleep when idle and doesn't wake up "instantly". Such behavior could not be implemented if everything on the SPI bus had to be synchronized to the main-logic clock (which wouldn't be running while the device was asleep).

PPS--If you really want to run everything synchronous with some "main" clock, you should figure that the setup and hold times of the incoming SPI data will be increased by at least one period of your master clock, and the outgoing data will be delayed by an unpredictable amount which may be anything from zero up to one period of your master clock. Your master clock needs to be fast enough that you can add a full master-clock period to your timing uncertainties without violating your timing constraints anywhere. If your allowable timing slop is 1/4 of your SPI clock period, your master clock needs to be more than 4x your SPI clock. If your allowable timing slop is 1/16 of your SPI clock period, your master clock needs to be more than 16x your SPI clock.

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

The advice to use the SPI clock to handle the SPI shift register is correct and saves you the bother of trying to oversample properly. One gotcha is in how you transfer that parallel register into another domain. DO NOT use a simple two stage FF synchronizer for each bit as you would for a solitary signal. If you use the FF synchronizers there is a real risk of invalid data clocking in the target domain due to skew or metastability on some bits. The paired FFs only make the probability of metastability exceedingly low but not zero. Assume it will happen.

Parallel busses need to be transferred across domains with a handshake synchronizer. For a fully-synchronous design that can be analyzed with static timing tools the four-phase handshake protocol is the best to use. In this protocol you have a pair of data-valid and ack signals that handshake across domains and guarantee that the bus is stable while the target domain clocks it in.

One exception to this rule is if you have a Gray encoded counter. Because only one bit transitions per count you can send it across domains with the simpler paired FFs.

\$\endgroup\$
11
  • 1
    \$\begingroup\$ The normal danger with using a two-stage FF for each bit is that some bits may change on an earlier clock than others; I would think that if metastability risk (as opposed to split latching) is unacceptable in that case, it would be equally unacceptable with solitary signals. On the other hand, I was wondering something: in an FPGA, is there any way of specifying that a signal must be ignored in certain cases? For example, if there is an input which may change at any time when some condition is true, but the circuit is only "supposed" to care about that signal when the condition is false... \$\endgroup\$
    – supercat
    Commented Jul 17, 2014 at 15:29
  • \$\begingroup\$ ...would there be a danger that the FPGA might rewrite things so that transitions on that signal when it wasn't supposed to matter might cause glitches downstream? For example, given the equation Q := (Q and not E) or (D and E) [D FF with enable] would there be a danger that a compiler might implement that using a non-hazard-free 3-input mux, such that a transition on D near the clock edge could glitch Q even when synchronous signal E was low? Is there any nice way to handle such things? \$\endgroup\$
    – supercat
    Commented Jul 17, 2014 at 15:35
  • 1
    \$\begingroup\$ @supercat For solitary asynchronous control signals the timing is generally loose enough that you can tolerate a one cycle slip. External interfaces must be designed to account for this possibility. If you want to ignore a signal under certain circumstances you just have to gate it (preferably after an initial synchronization stage). Once you have brought external signals into your controlled synchronous domain, the synthesizer can ensure that combinational paths between registers will meet timing. There will be no glitches to deal with if you follow proper synchronous design practices. \$\endgroup\$ Commented Jul 17, 2014 at 19:58
  • 1
    \$\begingroup\$ Most programmable logic devices don't implement logic using Boolean primitives, so while there are glitches during input transitions you have no way of reliably predicting what they are because you don't know the full implementation details. This is why you set up timing constraints and let the tools verify that the clock period is long enough to account for the longest combinational delay in the design or you make allowances for multi-cycle delays. \$\endgroup\$ Commented Jul 17, 2014 at 20:05
  • \$\begingroup\$ Suppose as a simple example that one has an FPGA which is supposed to read data from an external flash which requires one wait state, and it must take an externally-visible action on the second cycle after it outputs the request to the flash. It would seem absurd to require an external chip to ensure that the data inputs to the FPGA couldn't change near the first clock edge after the request was issued [since the flash shouldn't care what's on the bus at that time]; what would one have to do to avoid needing to do so? \$\endgroup\$
    – supercat
    Commented Jul 17, 2014 at 20:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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