Coder Social home page Coder Social logo

jakubcabal / spi-fpga Goto Github PK

View Code? Open in Web Editor NEW
159.0 16.0 38.0 2.85 MB

SPI master and SPI slave for FPGA written in VHDL

License: MIT License

VHDL 90.44% Tcl 3.39% Shell 6.18%
spi-slave spi-master fpga vhdl spi controller spi-loopback spi-controller cyc1000 accelerometer

spi-fpga's Introduction

SPI MASTER AND SLAVE FOR FPGA

The SPI master and SPI slave are simple controllers for communication between FPGA and various peripherals via the SPI interface. The SPI master and SPI slave have been implemented using VHDL 93 and are applicable to any FPGA.

The SPI master and SPI slave controllers support only SPI mode 0 (CPOL=0, CPHA=0)!

The SPI master and SPI slave controllers were simulated and tested in hardware. I use the GHDL tool for CI: automated VHDL simulations in the GitHub Actions environment (setup-ghdl-ci). If you have a question or an improvement tip, send me an e-mail or create an issue.

SPI master

Generics:

CLK_FREQ    : natural := 50e6; -- set system clock frequency in Hz
SCLK_FREQ   : natural := 5e6;  -- set SPI clock frequency in Hz (condition: SCLK_FREQ <= CLK_FREQ/10)
WORD_SIZE   : natural := 8;    -- size of transfer word in bits, must be power of two
SLAVE_COUNT : natural := 1     -- count of SPI slaves

Ports:

CLK      : in  std_logic; -- system clock
RST      : in  std_logic; -- high active synchronous reset
-- SPI MASTER INTERFACE
SCLK     : out std_logic; -- SPI clock
CS_N     : out std_logic_vector(SLAVE_COUNT-1 downto 0); -- SPI chip select, active in low
MOSI     : out std_logic; -- SPI serial data from master to slave
MISO     : in  std_logic; -- SPI serial data from slave to master
-- INPUT USER INTERFACE
DIN      : in  std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI slave
DIN_ADDR : in  std_logic_vector(natural(ceil(log2(real(SLAVE_COUNT))))-1 downto 0); -- SPI slave address
DIN_LAST : in  std_logic; -- when DIN_LAST = 1, last data word, after transmit will be asserted CS_N
DIN_VLD  : in  std_logic; -- when DIN_VLD = 1, data for transmission are valid
DIN_RDY  : out std_logic; -- when DIN_RDY = 1, SPI master is ready to accept valid data for transmission
-- OUTPUT USER INTERFACE
DOUT     : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI slave
DOUT_VLD : out std_logic  -- when DOUT_VLD = 1, received data are valid

Resource usage:

LE FF M9K Fmax
34 23 0 330.1 MHz

Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.

Simulation:

A simulation is prepared in the sim/ folder. You can use the prepared TCL script to run simulation in ModelSim.

vsim -do spi_master_tb_msim_run.tcl

Or it is possible to run the simulation using the GHDL tool. Linux users can use the prepared bash script to run the simulation in GHDL:

./spi_master_tb_ghdl_run.sh

SPI slave

Generics:

WORD_SIZE : natural := 8; -- size of transfer word in bits, must be power of two

Ports:

CLK      : in  std_logic; -- system clock
RST      : in  std_logic; -- high active synchronous reset
-- SPI SLAVE INTERFACE
SCLK     : in  std_logic; -- SPI clock
CS_N     : in  std_logic; -- SPI chip select, active in low
MOSI     : in  std_logic; -- SPI serial data from master to slave
MISO     : out std_logic; -- SPI serial data from slave to master
-- USER INTERFACE
DIN      : in  std_logic_vector(WORD_SIZE-1 downto 0); -- data for transmission to SPI master
DIN_VLD  : in  std_logic; -- when DIN_VLD = 1, data for transmission are valid
DIN_RDY  : out std_logic; -- when DIN_RDY = 1, SPI slave is ready to accept valid data for transmission
DOUT     : out std_logic_vector(WORD_SIZE-1 downto 0); -- received data from SPI master
DOUT_VLD : out std_logic  -- when DOUT_VLD = 1, received data are valid

Resource usage:

LE FF M9K Fmax
29 21 0 324.5 MHz

Implementation was performed using Quartus Prime Lite Edition 20.1.0 for Intel Cyclone 10 FPGA (10CL025YU256C8G) with default generics.

Simulation:

A simulation is prepared in the sim/ folder. You can use the prepared TCL script to run simulation in ModelSim.

vsim -do spi_slave_tb_msim_run.tcl

Or it is possible to run the simulation using the GHDL tool. Linux users can use the prepared bash script to run the simulation in GHDL:

./spi_slave_tb_ghdl_run.sh

Examples:

Spirit Level:

The Spirit Level example design shows one possible use of the SPI Master controller. The example design is prepared for FPGA board CYC1000 with Intel Cyclone 10 FPGA (10CL025YU256C8G) and digital accelerometer (LIS3DH). Here you can find the documentation of the CYC1000 board. In this design, the SPI Master controller is used to configure and read data from the accelerometer. The LEDs on the board show the values from the accelerometer in the form of a spirit level. You can watch the Spirit Level example video on YouTube.

Spirit Level example video

SPI loopback:

The SPI loopback example design allows testing transfers between SPI master and SPI slave over external wires. The example design is prepared for FPGA board EP4CE6 Starter Board with Altera FPGA Cyclone IV (EP4CE6E22C8), few buttons and a seven-segment display (four digit). You can watch the SPI loopback example video on YouTube.

Video of SPI loopback example design

Display description (from right on board in video):

Digit0 = value on SPI slave input
Digit1 = value on SPI slave output
Digit2 = value on SPI master input
Digit3 = value on SPI master output

Buttons description (from right on board in video):

BTN_ACTION (in mode0) = setup value on SPI slave input
BTN_ACTION (in mode1) = write (set valid) of SPI slave input value
BTN_ACTION (in mode2) = setup value on SPI master input
BTN_ACTION (in mode3) = write (set valid) of SPI slave input value and start transfer between SPI master and SPI slave
BTN_MODE = switch between modes (mode0 = light decimal point on digit0,...)
BTN_RESET = reset FPGA design

License:

This whole repository (include SPI master and SPI slave controllers) is available under the MIT license. Please read LICENSE file.

spi-fpga's People

Contributors

jakubcabal avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

spi-fpga's Issues

spi slave miso test

Hi again!
I am triying to write from the slave 4x data transfer of 16bit spi data field. This is executed every 5ms.
I am facing an "issue" with the reset state.

In order to handle the spi slave, I am using a state machine that checks the value of din_vld to set the value of din.
So the state machine waits to see an edge of din_vld to set a different value each time.

something like:

p_rising_edge_detector : process(i_clk, i_reset)
  begin
    if(i_reset='1') then
      r0  <= '0';
      r1  <= '0';
    elsif(rising_edge(i_clk)) then
      r0  <= i_din_rdy;
      r1  <= r0;
    end if;
  end process p_rising_edge_detector;
  ---------------------------------------
  edge <= not r1 and r0;
  ---------------------------------------

--------------------------------------------------------------------------------
state_machine_p: process (i_clk, i_reset)
-- Logic to advance to the next state
begin
  if i_reset = '1' then
    state <= s0;
  elsif (rising_edge(i_clk)) then
    case state is
      when s0=>
        if edge = '1' then --wait for slave not busy
          state <= s1;
        else
          state <= s0;
       end if;
      when s1=>
        if edge = '1' then --send first
          state <= s2;
        else
          state <= s1;
       end if;
      when s2=>
      ...
  end if;
end process;

What I see is that the slave is sending a 0x0000 first, and after the data I am sending back to the master.
I am unsure gow to force the slave to not to send this 0x0000 and start with my first 16bit data.

To put it more clear:

image

The first message I want to send is 0xCAFE, but it is not registered after the reset.
I am not sure how to handle this, as I see that in the first execution I am seeing 5 rising edges of din_rdy and after 4, as expected:

image

image

How should I handle it?
Thanks!

Maximum SCLK frequency on SPI SLAVE module

Hi, firstly your code is very clear and i'm learning a lot from this, so thanks!

Then I was reading in another issue that with a CLK frequency that is at least twice the SCLK frequency the code works fine.

I was trying to use a CLK at 12 MHz and a SCLK at 4 MHz and i found some problem with the MISO of the SPI SLAVE module.
Pratically the first 1 sended on the MISO remains too much on it and is sampled two times.

Screenshot 2023-08-09 172400

I'm using a simplified testbench that i can attach here.

SPI_SLAVE_tb.txt

There is something that I can do to fix this problem?

Support for different transfer size

Hi,

I have experimented with your code a little. I find it nice to read and learn from. For my purpose I need a larger transfer size (16 bits) perhaps that support for common transfer size (8,16,24,32) can be added?

Possible Meta-Stability Issue in Slave Module

The clock signal can potentially trigger meta-stability conditions.

Example is driving SCK from external pin while using significantly faster internal clock. Granted the internal versus external does not matter, however the setup time difference can cause metastable condition to exist. A hardware solution may prevent this using something like a Schmitt trigger.

I have only tried using the slave module on a Lattice Macho XO2. I was using a test signal of about 100kHz and an internal clock of about 133MHz. Simply adding a two register metastability circuit appeared to fix it. (I have only done limited testing with it.) I think this should probably be at least a feature given the amount of control signals that are derived from the ability to detect clock edges.

spi slave in hw => dout_vld no creating '1' value

Hi again,

After the simulation, I am implementing a simple spi slave in a Altera FPGA. I have wrapped the slave around a top module to assign pins. I have realized that the spi_slave does not rise the dout_vld flag:

I am using the next config:

  • CLK of 50Mhz
  • SPI SCLK of 5MHz.
  • I am using 16bit of data MOSI in each spi transaction.

I have performed a less sofisticated spi_slave_tb gate-level simulation inspired in yours, and looks to be fine.
I have plugged the Signal Tap Logic Analyzer and realize that the main difference is how MOSI behaves, in my case, its idle state is always '1':

image

Simulation capture:

image

I don't get why data_vld flag does not rise to '1' after each transaction... ๐Ÿค” ๐Ÿค”

A question about sclk edge

Hi man,

I have to say that your code looks so comfortable.

I'm a rookie of FPGA designing and have some questions hope that you can help:

  1. would that be a problem to shift data on rising edge of CLK together with checking spi_clk_redge_en level? As the spi_clk_redge_en is derived from spi_clk_reg, which is latch on rising edge of CLK as well, which means the spi_clk_redge_en might alter on rising edge of CLK.
  2. I have viewed many implementations of SPI slave, some of them use synchronized clock to latch or shift data in/out just like yours, while others use the sclk directly, which one would be better? and why?
  3. CAN CS_n be used as RST directly?

Not working on Cyclone II

Hi. I am trying to work with this library using an Altera Cyclone II, working at 50 Mhz.
I use an arduino as master. For debug I set a sclk of 100Khz or similar.
Arduino sends clock and data but miso does not change.
I have set data DIN_VLD to true and set a constant value of DIN input.

Nothing is received.
Thank you

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.