library IEEE; use IEEE.STD_LOGIC_1164.ALL; use IEEE.NUMERIC_STD.ALL; entity SPI_Slave is Generic ( breite: natural := 24); Port ( SCLK : in STD_LOGIC; SS : in STD_LOGIC; MOSI : in STD_LOGIC; MISO : out STD_LOGIC; Dout : out STD_LOGIC_VECTOR (breite-1 downto 0); Din : in STD_LOGIC_VECTOR (breite-1 downto 0)); end SPI_Slave; architecture Behavioral of SPI_Slave is signal dsr : STD_LOGIC_VECTOR (breite-1 downto 0); begin -- Parallel-Eingänge --> MISO process (SS, Din, SCLK) begin if (SS='1') then dsr <= Din; elsif rising_edge(SCLK) then -- mit der steigenden SCLK-Flanke dsr <= dsr(dsr'left-1 downto 0) & MOSI; -- wird MOSI eingetaktet end if; end process; MISO <= dsr(dsr'left) when SS='0' else 'Z'; -- und MISO aktualisiert -- Parallel-Ausgänge übernehmen mit steigender SS-Flanke process (SS) begin if rising_edge(SS) then -- Device wird deselektiert Dout <= dsr; -- Ausgangssignale an die Pins durchgeben end if; end process; end Behavioral;
Die Kommunikation findet im SPI-Modus 0 statt. Ruhezustand SCLK ist demnach LOW, die Daten werden an MOSI mit der steigenden Flanke eingelesen und sofort danach ändert sich MISO. Diese Implementierung braucht weniger FFs, weil das Schieberegister für das Eintakten und das Herausschieben zugleich verwendet wird.
Hier müssen aber vor dem Deselektieren alle Bits durchgetaktet werden, denn sonst werden Eingangsbits auf die Ausgänge ausgegeben. Wird z.B. nur SS kurz LOW und wieder HIGH, werden die Eingänge über das Schieberegister dsr auf die Ausgänge durchgegeben.
Auch hier gilt:
Diese Implementation ist für CPLDs ausgelegt! Für FPGAs ist die Umsetzung mit asynchronem Reset und zusätzlichen Taktdomänen (SS, SCLK) äusserst ungünstig. Hier sollte eher der Ansatz mit einer Überabtaktung des SCLK mit anschliessender Flankenerkennung gewählt werden. Etwa so wie dort auf dem uC.net im Beitrag Erfahrung mit SPI Slave und Spartan 6 FPGA?