FPGA:RaspberryPi:SPI:En

From Retro-CPU.run
Revision as of 09:43, 28 August 2016 by Old (Talk | contribs)

(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to: navigation, search

SPI / serial data transfer from Raspberry Pi to FPGA.

Note: This is not a full SPI implementation. Please refer to https://en.wikipedia.org/wiki/Serial_Peripheral_Interface_Bus#Valid_communications for further information on the SPI protocol.


For assistance in using or adapting this work, please go to https://www.facebook.com/groups/retrocpu/

This project implements the following:

-----|             |----
     |             |
     |----sclk---->|  F
 P   |---- cs----->|  P
 I   |----mosi---->|  G
     |<----miso----|  A
     |             |
-----|             |----

Therefore only 4 pins are be needed on both the PI and on the FPGA. The project implements a sample 32 bit data transfer, which can be used as a basis for more complex projects.

The C code for the Pi follows:

/*
 
shiftregister-transfer.c
Ronivon C. Costa, 2016.
 
PI pinout x GPIO:
http://abyz.co.uk/rpi/pigpio/index.html


Library required by this program and how to install:
http://abyz.co.uk/rpi/pigpio/download.html

Steps:
wget abyz.co.uk/rpi/pigpio/pigpio.tar
tar xf pigpio.tar
cd PIGPIO
make -j4
sudo make install

To compile and run this program:
cc -Wall -pthread -o shiftregister-transfer shiftregister-transfer.c -lpigpio -lrt

sudo ./shiftregister-transfer

 */

#include <stdio.h>
#include <pigpio.h>

/* GPIO pin numbers used in this program */
#define cs    1
#define sclk  7
#define mosi  8
#define miso  9

#define SPI_SCLK_LOW_TIME 1
#define SPI_SCLK_HIGH_TIME 1 
#define HIGH 1
#define LOW 0

void write_CS(unsigned char bit) {
   gpioWrite(cs, bit);
}

void write_MOSI(unsigned char bit) {
   gpioWrite(mosi, bit);
}

void write_SCLK(unsigned char bit) {
   gpioWrite(sclk, bit);
}

unsigned char read_MISO() {
   return gpioRead(miso);
}

void delay(int t) {
   t;
}

unsigned char SPI_transfer_byte(unsigned char byte_out) {
   unsigned char byte_in = 0;
   unsigned char bit;
   
   for (bit = 0x80; bit; bit >>=1) {
       write_MOSI((byte_out & bit) ? HIGH : LOW);
       delay(SPI_SCLK_LOW_TIME);
       write_SCLK(HIGH);
       if (read_MISO() == HIGH)
           byte_in |= bit;
       delay(SPI_SCLK_HIGH_TIME);
       write_SCLK(LOW);
   }
   
   return byte_in;
    
}

void init_spi_bitbang(void) {
   /* Set GPIO modes */
   /* Usable pins on left row Pi3 */
   gpioSetMode(cs, PI_OUTPUT);
   gpioSetMode(sclk, PI_OUTPUT);
   gpioSetMode(mosi, PI_OUTPUT);
   gpioSetMode(miso, PI_INPUT);
}

void send_block_address(unsigned char b3,unsigned char b2,unsigned char b1,unsigned char b0) {
   unsigned char bytein;
    
   bytein = SPI_transfer_byte(b3);
   printf("Returned by SPI:%hhu\n", bytein);
   bytein = SPI_transfer_byte(b2);
   printf("Returned by SPI:%hhu\n", bytein);
   bytein = SPI_transfer_byte(b1);
   printf("Returned by SPI:%hhu\n", bytein);
   bytein = SPI_transfer_byte(b0);
   printf("Returned by SPI:%hhu\n", bytein);
}

int main(int argc, char *argv[])
{
    
   if (gpioInitialise() < 0)
   {
       fprintf(stderr, "pigpio initialisation failed\n");
       return 1;
   }
   
   init_spi_bitbang();
   printf("GPIO Initialized\n");
   
   printf("Enabling CS\n");
   write_CS(1);
   delay(SPI_SCLK_LOW_TIME);
   write_SCLK(HIGH);
   delay(SPI_SCLK_HIGH_TIME);
   write_SCLK(LOW);
   
   printf("Sending 0x02, 0xfe, 0xaa, 0x0f\n");
   send_block_address(0x02, 0xfe, 0xaa, 15);
   
   printf("Disabling CS\n");
   write_CS(0);
   delay(SPI_SCLK_LOW_TIME);
   write_SCLK(HIGH);
   delay(SPI_SCLK_HIGH_TIME);
   write_SCLK(LOW);
   
   getchar();
   printf("Enabling CS for SPI device\n");
   write_CS(1);
   delay(SPI_SCLK_LOW_TIME);
   write_SCLK(HIGH);
   delay(SPI_SCLK_HIGH_TIME);
   write_SCLK(LOW);
   
   printf("Sending 0xFF, 0xa0, 0x11, 0x10\n");
   send_block_address(0xFF, 0xa0, 0x11, 0x10);
   
   write_CS(0);
   delay(SPI_SCLK_LOW_TIME);
   write_SCLK(HIGH);
   delay(SPI_SCLK_HIGH_TIME);
   write_SCLK(LOW);
   
   /* Stop DMA, release resources */
   
   printf("Terminating GPIO\n");
   gpioTerminate();
   
   return 0;
}

And the VHL implementation of the shift register follows. Note that the bulk of the design is actually related to displaying the data in the 7 segment display. The actual transfer protocol is contained in the process and controlled by a simple state machine with three states only.

serial_transfer.vhd
Ronivon C. Costa, 2016.

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

entity serial_transfer is
port (
    pi_cs : in std_logic;
    pi_mosi : in std_logic;
    pi_miso : out std_logic;
    pi_clk : in std_logic;
    HEX0, HEX1, HEX2, HEX3, HEX4, HEX5, HEX6, HEX7 : out std_logic_vector(6 downto 0) -- 7-segment displays
);

end serial_transfer;

architecture rtl of serial_transfer is

component decoder_7seg
port (
    NUMBER                  : in   std_logic_vector(3 downto 0);
    HEX_DISP                : out  std_logic_vector(6 downto 0));
end component;
	
signal reset   : std_logic;
signal pi_cs   : std_logic;
signal pi_mosi : std_logic;
signal pi_miso : std_logic;
signal pi_sclk : std_logic;
signal blkaddr : std_logic_vector(31 downto 0) := "00000000000000000000000000000000";
signal NUMBER0              : std_logic_vector(3 downto 0);
signal NUMBER1              : std_logic_vector(3 downto 0); 
signal NUMBER2              : std_logic_vector(3 downto 0);
signal NUMBER3              : std_logic_vector(3 downto 0);
signal NUMBER4              : std_logic_vector(3 downto 0);
signal NUMBER5              : std_logic_vector(3 downto 0); 
signal NUMBER6              : std_logic_vector(3 downto 0);
signal NUMBER7              : std_logic_vector(3 downto 0);
signal HEX_DISP0            : std_logic_vector(6 downto 0);
signal HEX_DISP1            : std_logic_vector(6 downto 0);
signal HEX_DISP2            : std_logic_vector(6 downto 0);
signal HEX_DISP3            : std_logic_vector(6 downto 0);
signal HEX_DISP4            : std_logic_vector(6 downto 0);
signal HEX_DISP5            : std_logic_vector(6 downto 0);
signal HEX_DISP6            : std_logic_vector(6 downto 0);
signal HEX_DISP7            : std_logic_vector(6 downto 0);
	
begin
		 	
    HEX0 <= HEX_DISP0;
    HEX1 <= HEX_DISP1;
    HEX2 <= HEX_DISP2;
    HEX3 <= HEX_DISP3;
    HEX4 <= HEX_DISP4;
    HEX5 <= HEX_DISP5;
    HEX6 <= HEX_DISP6;
    HEX7 <= HEX_DISP7;
	
    process(pi_sclk,pi_cs)
    type fsm_spi is (idle, fsm_addr, completed);
	
    variable spi_state : fsm_spi := idle;
    variable addrcount : natural range 0 to 31 := 0;
    variable ledgv : std_logic_vector(7 downto 0) := "00000000";
    variable ledrv : std_logic_vector(17 downto 0) := "000000000000000000";
    variable NUMBER0_sig : std_logic_vector(3 downto 0);
    variable NUMBER1_sig : std_logic_vector(3 downto 0); 
    variable NUMBER2_sig : std_logic_vector(3 downto 0);
    variable NUMBER3_sig : std_logic_vector(3 downto 0);
    variable NUMBER4_sig : std_logic_vector(3 downto 0);
    variable NUMBER5_sig : std_logic_vector(3 downto 0); 
    variable NUMBER6_sig : std_logic_vector(3 downto 0);
    variable NUMBER7_sig : std_logic_vector(3 downto 0);
	
	begin

	if rising_edge(pi_sclk) then
		case spi_state is
			when idle => 
			    if pi_cs = '1' then
					spi_state := fsm_addr;
			    end if;
			when fsm_addr =>
			    blkaddr <= blkaddr(30 downto 0) & pi_mosi;
			 	 pi_miso <= pi_mosi; -- for test return same bit
				 addrcount := addrcount + 1;
				 if addrcount = 0 then
				     spi_state := completed;
				 end if;
			when completed =>
			    NUMBER0_sig := blkaddr(3 downto 0);
				NUMBER1_sig := blkaddr(7 downto 4);
				NUMBER2_sig := blkaddr(11 downto 8);
				NUMBER3_sig := blkaddr(15 downto 12);
				NUMBER4_sig := blkaddr(19 downto 16);
				NUMBER5_sig := blkaddr(23 downto 20);
				NUMBER6_sig := blkaddr(27 downto 24);
				NUMBER7_sig := blkaddr(31 downto 28);
				spi_state := idle;
		end case;
	end if;
	
	NUMBER0           <= NUMBER0_sig;
    NUMBER1           <= NUMBER1_sig;
    NUMBER2           <= NUMBER2_sig;
    NUMBER3           <= NUMBER3_sig; 
    NUMBER4           <= NUMBER4_sig;
    NUMBER5           <= NUMBER5_sig;
    NUMBER6           <= NUMBER6_sig;
    NUMBER7           <= NUMBER7_sig;
	
end process;
	
    DISPHEX0 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER0,
        HEX_DISP                =>  HEX_DISP0
    );      

    DISPHEX1 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER1,
        HEX_DISP                =>  HEX_DISP1
    );      

    DISPHEX2 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER2,
        HEX_DISP                =>  HEX_DISP2
    );      

    DISPHEX3 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER3,
        HEX_DISP                =>  HEX_DISP3
	);
	
	DISPHEX4 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER4,
        HEX_DISP                =>  HEX_DISP4
    );      

    DISPHEX5 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER5,
        HEX_DISP                =>  HEX_DISP5
    );      

    DISPHEX6 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER6,
        HEX_DISP                =>  HEX_DISP6
    );      

    DISPHEX7 : decoder_7seg 
	port map (
        NUMBER                  =>  NUMBER7,
        HEX_DISP                =>  HEX_DISP7
	);
	
end rtl;

The supporting 7 Segment decode is this:

-------------------------------------------------------------------------------------------------
-- This design is part of:
-- Z80SoC (Z80 System on Chip)
-- Ronivon Candido Costa
-- ronivon.costa@gmail.com
--

LIBRARY IEEE;
USE  IEEE.STD_LOGIC_1164.all;
USE  IEEE.STD_LOGIC_UNSIGNED.all;

entity decoder_7seg is
	port
	(
		NUMBER		: in   std_logic_vector(3 downto 0);
		HEX_DISP	: out  std_logic_vector(6 downto 0)
	);
end decoder_7seg;

architecture rtl of decoder_7seg is
begin
process(NUMBER)
begin
	case NUMBER is
		--0 to 9
		when "0000" => HEX_DISP <= "1000000";
		when "0001" => HEX_DISP <= "1111001";
		when "0010" => HEX_DISP <= "0100100";
		when "0011" => HEX_DISP <= "0110000";
		when "0100" => HEX_DISP <= "0011001";
		when "0101" => HEX_DISP <= "0010010";
		when "0110" => HEX_DISP <= "0000011";
		when "0111" => HEX_DISP <= "1111000";
		when "1000" => HEX_DISP <= "0000000";
		when "1001" => HEX_DISP <= "0011000";
		-- A to F
		when "1010" => HEX_DISP <= "0001000";
		when "1011" => HEX_DISP <= "0000011";
		when "1100" => HEX_DISP <= "1000110";
		when "1101" => HEX_DISP <= "0100001";
		when "1110" => HEX_DISP <= "0000110";
		when "1111" => HEX_DISP <= "0001110";
		when others => HEX_DISP <= "1111111";
	end case;
end process;
end rtl;