For the core SPI lets keep it brief, the SPI interface is basically just a shift register and it combines the transmitter and receiver. The clock comes from an external source so resynchronization needs to be done but I would argue that belongs outside of the SPI component; normally what I do is make a couple holding registers and use a single phase semaphore (t-flop).
It is better to use a uart if there is a reliable clock available, the SPI clock is asynchronous and unpredictable.
Here is my SPI block, it has been in several ASICs. There was once a schematic version. There are a couple signals to the external interface to trigger command interpretation or writing the data. Feel free to use it as GPL2
-- spi_block.vhd is the SPI communication interface
-- simple 8 bit interface.
-- CPOL=0, CPHA=1 (shift output on low edge, capture rising edge)
-- Note: first bit is high bit which is driven when CSB goes low.
-- ldcr=1 on load cycle, cyc_6=1 on interpret cycle before this
-- ld data will parallel load on ldcr=1 cycle.
--
LIBRARY IEEE;
USE IEEE.std_logic_1164.all;
USE IEEE.numeric_std.all;
use work.misc_pkg.all;
ENTITY spi_block IS PORT (
OBUF: OUT byte;
CR : OUT byte;
ld : IN byte;
SCK : IN std_logic;
MOSI : IN std_logic;
CSB : IN std_logic;
MISO : OUT std_logic;
ldcr : OUT std_logic;
cyc_6 : OUT std_logic
); END spi_block;
architecture rtl of spi_block is
-- output mapping
signal CRi,mx : byte;
signal misoi,ldcri,cyc_6i : std_logic;
signal miso_d:std_logic; -- init by POR
-- internal signals
signal ct: std_logic_vector(2 downto 0); -- 8 count
signal sh_ldn : std_logic;
begin
-- mappings:
OBUF<=mx;
MISO <= misoi after 2 ns;
ldcr <= ldcri;
cyc_6 <= cyc_6i;
CR <= CRi after 2 ns;
CRi(0) <= MOSI;
CRi(7 downto 1) <= mx(6 downto 0);
ldcri <= \'1\' when ct = \"111\" else \'0\';
cyc_6i <= \'1\' when ct = \"110\" else \'0\';
sh_ldn <= \'0\' when ct = \"000\" else \'1\';
miso_d<=ld(7) when sh_ldn=\'0\' else mx(7); -- mux for miso data at start
-- Count the bits output on falling edge, parallel load when =\"111\"
process(SCK,CSB) begin
if (CSB=\'1\') then
ct <= (others=>\'0\') ;
elsif (SCK\'event and SCK=\'0\') then
ct <= std_logic_vector(signed(ct) + 1) ;
end if;
end process;
-- use a latch for MISO to provide setup and hold for the master.
-- make sure this synthesizes a latch
process(SCK,miso_d)
begin -- MISO latch
if SCK = \'0\' then
misoi<=miso_d;
end if;
end process;
-- IO is a parallel load shift register.
-- this will be done by gated register.
process(SCK) begin
if (SCK\'event and SCK=\'1\') then -- load and shift on + edge
if CSB=\'0\' then
mx(0) <= mosi;
if sh_ldn=\'0\' then -- load at first clock
mx(7 downto 1) <= ld(6 downto 0);
else
mx(7 downto 1) <= mx(6 downto 0);
end if;
end if;
end if;
end process;
This site uses cookies to help personalise content, tailor your experience and to keep you logged in if you register.
By continuing to use this site, you are consenting to our use of cookies.