«

»

Aug 19

FPGA: SPI Controller

The Serial Peripheral Interface (SPI) Bus is a full duplex synchronous serial communication standard. SPI is common in embedded applications due to it’s high throughput and relatively simple interface. This is a simple SPI controller implementation with an 8 bit word length and a single slave select line.

The ability to change data word length is built in to the code. Support for additional slave devices can be added with simple modifications.
An 8 bit counter from a previous example is used to provide input to the SPI controller. The Master Out Slave In (MOSI) line is connected directly back into the Slave Out Master In line. This allows comparison of the RX data line to the counter input. The logic analyzer screenshot shows that the data is clocked out, and back in, as expected. Further testing with a real SPI device will be required to verify the timings are adequate.

SPI_Controller.vhd

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
LIBRARY IEEE;
USE IEEE.STD_LOGIC_1164.all;
USE IEEE.STD_LOGIC_ARITH.all;
USE IEEE.STD_LOGIC_UNSIGNED.all;

ENTITY SPI_Controller IS
  GENERIC(
    wordLength : INTEGER := 8
  );
  PORT (
    clock   : IN  STD_LOGIC;
    reset   : IN  STD_LOGIC;
    enable  : IN  STD_LOGIC;
    busy    : OUT STD_LOGIC;
    tx_data : IN  STD_LOGIC_VECTOR(wordLength-1 DOWNTO 0);
    rx_data : OUT STD_LOGIC_VECTOR(wordLength-1 DOWNTO 0);
    mode    : IN  STD_LOGIC_VECTOR(1 DOWNTO 0);

    sclk    : BUFFER STD_LOGIC;
    mosi    : OUT    STD_LOGIC;
    miso    : IN     STD_LOGIC;
    ss      : BUFFER STD_LOGIC
  );
end SPI_Controller;

ARCHITECTURE structure of SPI_Controller IS
  SIGNAL clockCount : INTEGER RANGE 0 TO wordLength*2+2;
  SIGNAL sPhase : STD_LOGIC;
  SIGNAL txBuffer : STD_LOGIC_VECTOR(wordLength-1 DOWNTO 0);
  SIGNAL rxBuffer : STD_LOGIC_VECTOR(wordLength-1 DOWNTO 0);

BEGIN
  PROCESS(clock, reset)
  BEGIN
    if reset = '1' then
      busy <= '1';
      sclk <= mode(1);
      sPhase <= mode(0);
      txBuffer <= tx_data;
    elsif (clock'EVENT and clock = '1') then
      if enable = '1' then
        if clockCount < wordLength*2 then
           if sclk = sPhase then
             mosi <= txBuffer(wordLength-1);
             txBuffer <= txBuffer(wordLength-2 DOWNTO 0) & '0';
           else
             rxBuffer <= rxBuffer(wordLength-2 DOWNTO 0) & miso;
           end if;
           sclk<= NOT sclk;
           clockCount <= clockCount + 1;
        elsif clockCount = wordLength*2 then
          ss <= '1';
          busy <= '0';
          clockCount <= clockCount + 1;
        elsif clockCount = wordLength*2+1 then
          txBuffer <= tx_data;
          rx_data <= rxBuffer;
          clockCount <= clockCount + 1;
        elsif clockCount = wordLength*2+2 then
          mosi <= txBuffer(wordLength-1);
          ss <= '0';
          busy <= '1';
          clockCount <= 0;
        end if;
      end if;
    end if;
  end process;
end structure;

Sources:
Serial Peripheral Interface Bus
Serial Peripheral Interface (SPI) Master (VHDL)
Related Posts:
FPGA: 4-Bit Counter
Downloads:
SPI Controller (496)

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>