Well, I checked and my CLK is indeed being routed thru GCLK3; a global clock. So that's probably not the problem.
Now, I made a test. I've made the FPGA copy the received SPI_CLK and SPI_MOSI to two other output signals upon a SPI_CLK rising edge detection, and copy the received SPI_CS to another output signal upon an SPI_CS event.
Then, using a logic analyzer, I captured the SPI CS, CLK and MOSI directly from the CPU which are entering the FPGA, and the SPI CS, CLK and MOSI that the FPGA is outputting as the copies of those signals. The result was this:
https://www.alteraforum.com/forum/attachment.php?attachmentid=13964 The FPGA is receiving those perfectly. I checked pages and pages of captured data, and the FPGA is outputting exactly what it's receiving, with the expected shift in the SPI_MOSI copy, which since the FPGA captures it only upon a rising SPI_CLK, will only be copied at that event. So, everything is as expected.
Looking at that, it doesn't look like its a metastability issue. There's plenty of Tsu and Th for SPI_MOSI.
Now, the weird part is: I made a counter of how many rising SPI_CLK edges are being received per SPI_CS low period. This counter increments inside the same IF that copies SPI_MOSI to the output pin and shifts it into the array, and is cleared when SPI_CS goes low (falling edge; start of a new set of SPI data). I've then made the 8 LEDs on my development kit show the value of this counter on every rising edge of SPI_CS (when a new set of SPI data has been fully received). See the lines in bold on the code below:
ENTITY Video IS
PORT ( CLK : IN STD_LOGIC; -- Main FPGA clock (50MHz XTAL). It's set on the Pin Planner as simply 3.3-V LVTTL / 8mA. Placed on a clock input pin
SPI_CS : IN STD_LOGIC;
SPI_CLK : IN STD_LOGIC;
SPI_MOSI : IN STD_LOGIC;
LEDS : OUT STD_LOGIC_VECTOR(7 downto 0)); -- LEDS on the development board
END Video;
ARCHITECTURE Refresh OF Video IS
signal SPI_IN : STD_LOGIC_VECTOR (14 DOWNTO 0); -- Received SPI data is shifted here, MSB first
signal SPI_CLK_LAST : STD_LOGIC :='0';
signal SPI_CS_LAST : STD_LOGIC :='0';
BEGIN
PROCESS (CLK)
BEGIN
IF rising_edge(CLK) THEN
IF SPI_CS='0' THEN
IF SPI_CS_LAST='1' THEN
SPI_IN_CNT<="00000000";
SPI_CS_LAST<='0';
end if;
if SPI_CLK='1' and SPI_CLK_LAST='0' then
SPI_CLK_LAST<='1';
SPI_IN<=SPI_IN(13 downto 0) & SPI_MOSI;
SPI_IN_CNT<=SPI_IN_CNT+1;
elsif SPI_CLK='0' then
SPI_CLK_LAST<='0';
end if;
ELSIF SPI_CS='1' and SPI_CS_LAST='0' THEN
SPI_CS_LAST<='1';
-- Here I'll process the data received in SPI_IN
LEDS<=SPI_IN_CNT;
END IF;
END IF;
END PROCESS;
By what I'm seeing on my logic analyzer, the LEDs should always show 12, as there are 12 rising clock edges per SPI_CS low period, right? Nope. The LEDs are showing all sorts of values between ~3 and ~67 (guessing the values I've seen on the LEDs - I made a HOLD button to freeze the LEDs at the last final value).
What could it be then? Could it still be metastability?