Search code examples
interfacesynchronizationvhdlfpgaspi

Input Signal Edge Detection on FPGA


I am trying to interface a Virtex 4 (ML401) FPGA and a TIVA C series board using 4 wire SPI (cs, sclk, miso, mosi). The tiva acts as a master and the FPGA as a slave. I am able to receive the SPI data from the master and display the data on the LEDS present on the FPGA (Method 2). However, I need to find the rising and falling transitions of the chip select signal (required for my application for synchronization purposes). I have tried many methods with FIFO's (that work well in simulation) but just don't work on the FPGA, as shown:

NOTE: spi_cs is the asynchronous SPI chip select signal input to the FPGA from the TIVA board, while other signals (spi_cs_s, spi_cs_ss, spi_cs_h2l, spi_cs_l2h, etc) are created internally on the FPGA.

Method 1)

                prc_sync_cs: process(clk)
                begin
                    if (clk'event and clk = '1') then
                        spi_cs_s  <= spi_cs;
                end if; 
                end process prc_sync_cs;

                spi_cs_l2h <= not (spi_cs_s) and spi_cs;
                spi_cs_h2l <= not (spi_cs) and spi_cs_s;

Method 2)

                process (spi_cs)
                begin
                    if (spi_cs = '0' or spi_cs = '1')  then
                        -- update ledss with new MOSI on rising edge of CS
                        spi_cs_ss <= spi_cs_s;
                        spi_cs_s  <= spi_cs;
                        --leds <= spi_wdata; --leds display the received data on the FPGA (saved into spi_wdata in another process)
                                            -- THIS WORKS ON THE FGPA BUT the edge detection doesn't. Why?
                    end if;
                end process;

                spi_cs_h2l <= '1' when (spi_cs_s = '0' and spi_cs_ss = '1') else '0';
                spi_cs_l2h <= '1' when (spi_cs_s = '1' and spi_cs_ss = '0') else '0';
                leds <= "000000" & spi_cs_h2l & spi_cs_l2h; -- ALL leds are off always (i,e both transitions are '0' always).

Method 3)

                prc_sync_cs: process(clk)
                begin
                    if (clk'event and clk = '1') then
                        spi_cs_ss <= spi_cs_s;
                        spi_cs_s  <= spi_cs;
                    end if; 
                end process prc_sync_cs;

                prc_edge_cs: process(clk)
                begin
                    if (clk'event and clk = '1') then
                        spi_cs_ss_del <= spi_cs_ss;
                    end if; 
                end process prc_edge_cs;

                spi_cs_h2l <= '1' when (spi_cs_ss_del = '1' and spi_cs_ss = '0') else '0';
                spi_cs_l2h <= '1' when (spi_cs_ss_del = '0' and spi_cs_ss = '1') else '0';

ALL the methods work perfectly in simulation but not when downloaded on the FPGA. I wrote a process to monitor the transitions more closely (to monitor metastable values, if any):

            led_test: process(spi_cs_h2l, spi_cs_l2h)
            begin
                if spi_cs_h2l = '1' or spi_cs_l2h = '1' then
                                leds <= "111100" & spi_cs_h2l & spi_cs_l2h;
                elsif spi_cs_h2l = 'X' or spi_cs_h2l = 'U' or spi_cs_h2l = 'Z' or
                        spi_cs_l2h = 'X' or spi_cs_l2h = 'U' or spi_cs_l2h = 'Z' then
                                leds <= "00001111";         
                else
                    leds <= "10101010";
                end if;
            end process led_test;

The leds are always "10101010" i.e is the else case where both spi_cs_h2l and spi_cs_l2h are = '0'. What am I missing ?? Any pointers would be very helpful as I am stuck with this issue since many days.

UPDATE

Using method 3 of clock domain crossing (as suggested by Jeff), and by initializing all the leds and signals to zero, the process to light up the leds is changed as follows:

            led_test: process(spi_cs_h2l)
            begin
                if rising_edge(clk) then
                    if spi_cs_h2l = '1' then
                                leds <= "11110011";
                    end if;
                end if; 
            end process led_test;

At least one high to low transition of the chip select pin is expected to light up the leds. The SPI chip select pin is receiving a '1' always and when FPGA is started/reset, the leds light up. How is this possible? How can this false high to low transition occur ?


Solution

  • Method 1

    This does not perform any sort of clock domain crossing on the spi_cs signal, and so is not a reliable circuit.

    Method 2

    The line if (spi_cs = '0' or spi_cs = '1') then is always true in the synthesised design, I wouldn't expect you to be able to detect an edge using this

    Method 3

    This does provide clock domain crossing for spi_cs, and in general looks pretty good. The reason you see "10101010" on your LEDs, is because they only show something different to this for one clk period at a time, at the start or end of an SPI transaction. This is probably much faster than you can see with the naked eye on the LEDs.

    Additionally, the line elsif spi_cs_h2l = 'X' or spi_cs_h2l = 'U' or spi_cs_h2l = 'Z' or spi_cs_l2h = 'X' or spi_cs_l2h = 'U' or spi_cs_l2h = 'Z' then will not translate into any real hardware in the FPGA, because the real hardware does not have a way to check for 'U', 'Z', etc.

    Method 3 update

    It sounds like spi_cs is actually active low. You need to make sure that the initial values for your signals like spi_cs_s and spi_cs_ss are all correct. In this case, I think you should initialise them all to '1', as this seems to be the normal state for spi_cs. So your signal declarations would look like signal spi_cs_s : std_logic := '1'. You should be able to see this behaving properly in simulation.