0
0
VhdlHow-ToIntermediate · 4 min read

VHDL Code for SPI Slave: Syntax, Example, and Tips

A basic SPI slave in VHDL uses signals for SCLK, MOSI, MISO, and SS. The slave reads data on the clock edges when SS is active and shifts data out on MISO. This requires a state machine to track bits and synchronize communication.
📐

Syntax

An SPI slave in VHDL typically has these ports:

  • SCLK: Serial clock input from master
  • MOSI: Master Out Slave In data input
  • MISO: Master In Slave Out data output
  • SS: Slave select input (active low)
  • clk: Internal clock for logic synchronization
  • rst: Reset signal

The main process triggers on the rising edge of SCLK when SS is low to shift in data bits and shift out response bits.

vhdl
entity spi_slave is
    Port (
        clk   : in  std_logic;
        rst   : in  std_logic;
        sclk  : in  std_logic;
        mosi  : in  std_logic;
        miso  : out std_logic;
        ss    : in  std_logic
    );
end spi_slave;
💻

Example

This example shows a simple SPI slave that receives 8 bits from the master and sends back the same byte incremented by 1. It uses a shift register and a bit counter.

vhdl
library ieee;
use ieee.std_logic_1164.all;
use ieee.numeric_std.all;

entity spi_slave is
    Port (
        clk   : in  std_logic;
        rst   : in  std_logic;
        sclk  : in  std_logic;
        mosi  : in  std_logic;
        miso  : out std_logic;
        ss    : in  std_logic
    );
end spi_slave;

architecture Behavioral of spi_slave is
    signal bit_count : integer range 0 to 7 := 0;
    signal shift_reg_in  : std_logic_vector(7 downto 0) := (others => '0');
    signal shift_reg_out : std_logic_vector(7 downto 0) := (others => '0');
    signal sclk_prev : std_logic := '0';
begin
    process(clk, rst)
    begin
        if rst = '1' then
            bit_count <= 0;
            shift_reg_in <= (others => '0');
            shift_reg_out <= (others => '0');
            miso <= '0';
            sclk_prev <= '0';
        elsif rising_edge(clk) then
            sclk_prev <= sclk;
            if ss = '0' then  -- Active low slave select
                -- Detect rising edge of SCLK
                if sclk_prev = '0' and sclk = '1' then
                    -- Shift in MOSI bit
                    shift_reg_in <= shift_reg_in(6 downto 0) & mosi;
                    -- Shift out MSB bit on MISO
                    miso <= shift_reg_out(7);
                    -- Shift left output register
                    shift_reg_out <= shift_reg_out(6 downto 0) & '0';
                    if bit_count = 7 then
                        bit_count <= 0;
                        -- Prepare next output: increment received byte
                        shift_reg_out <= std_logic_vector(unsigned(shift_reg_in) + 1);
                    else
                        bit_count <= bit_count + 1;
                    end if;
                end if;
            else
                bit_count <= 0;
                miso <= '0';
            end if;
        end if;
    end process;
end Behavioral;
Output
No direct console output; the SPI slave shifts in 8 bits on SCLK rising edges when SS is low and outputs incremented data on MISO.
⚠️

Common Pitfalls

Common mistakes when writing an SPI slave in VHDL include:

  • Not synchronizing SCLK to the internal clock domain, causing metastability.
  • Ignoring the SS signal, which disables the slave when high.
  • Shifting data on the wrong clock edge (SPI mode matters).
  • Not resetting counters and registers properly.

Always detect clock edges carefully and handle SS to avoid data corruption.

vhdl
Wrong approach (shifting on internal clk without SCLK edge detection):
process(clk)
begin
  if rising_edge(clk) then
    shift_reg <= shift_reg(6 downto 0) & mosi; -- Incorrect: ignores SCLK
  end if;
end process;

Right approach (detect SCLK rising edge):
process(clk)
begin
  if rising_edge(clk) then
    if sclk_prev = '0' and sclk = '1' then
      shift_reg <= shift_reg(6 downto 0) & mosi; -- Correct
    end if;
  end if;
end process;
📊

Quick Reference

Tips for SPI slave in VHDL:

  • Use a process synchronized to your system clock.
  • Detect edges of SCLK inside that process.
  • Use SS to enable/disable shifting.
  • Shift data in on one clock edge and shift data out on the opposite edge if needed.
  • Reset counters and registers on reset or when SS goes high.

Key Takeaways

Detect SCLK edges inside a clocked process to safely shift data in and out.
Use the SS signal to enable the SPI slave only when selected.
Shift registers hold incoming and outgoing data synchronized with SCLK.
Reset all counters and registers properly to avoid undefined states.
Test SPI modes carefully as clock polarity and phase affect data timing.