From Rob Anderson@21:1/5 to All on Tue Aug 11 17:07:45 2020
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.
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;
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) ;
-- use a latch for MISO to provide setup and hold for the master.
-- make sure this synthesizes a latch
begin -- MISO latch
if SCK = '0' then
-- IO is a parallel load shift register.
-- this will be done by gated register.
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);
mx(7 downto 1) <= mx(6 downto 0);