• Code Review: SPI Transmitter

    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.
    --
    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;

    end rtl; -- spi_block

    --- SoupGate-Win32 v1.05
    * Origin: fsxNet Usenet Gateway (21:1/5)