HomeS-100 Boards HistoryNew BoardsSoftwareBoards For Sale
ForumOther Web SitesNewsIndex   
An SPI Master Controller Interface Demo using a Cyclone IV FPGA.
Here we will improve on our earlier FPGA SPI interface and use it to communicate with a variable number of of data bits. This can be 8, 16, 24, 32, 40 or 48 bits.  Long SPI continuous data streams are for example requires to interface with SPI SD Cards. These require 6 byte commands.  Unlike the I2C interface the SPI clock cannot be stretched. You must have all the data bytes setup before you start. The good news however is for most SPI chips,  you can delay getting data to the chip if you just hold CS* (Chip select) low until everything is done. If you have not done so already, please read the introduction to the SPI interface before going further.

To recap, to begin communication, the bus master starts the clock, using a frequency supported by the slave device. Typically this can be in the 100's of KHz range. The master then selects the appropriate slave device by lowering that slaves SS input. If a waiting period is required, such as in for an analog-to-digital conversion, the master must wait for at least that period of time before issuing clock cycles.  This has to be agreed upon in the programming software. The master then sends a bits on the MOSI line and the slave reads them. At the same time the slave sends bits on the MISO line to the master. This sequence is maintained even when only one-directional data transfer is intended. I.e to read a byte you must send a byte. Its important to appreciate two things.
1.   The sending and receiving data to/from the slave is completely linked. There is just one loop like circuit with two shift registers (see below).
2.   The actual data format is completely determined by the software in the master and slave.  The number of bits transmitted can be 8, 16, 32, 48 etc.  In theory any number.  

While the most common format is data are 16 bit packages, and that is what we used in our first FPGA SPI example.  There are many devices that require more data bytes/commands. 
What is needed is a more generalized SPI interface with no limit on the number of outgoing or incoming data bytes. Such an SPI interface can be used for numerous SPI chips.

There are numerous examples of building an FPGA SPI interface on the web.   As a beginner, frankly I found a number of them overly complex and/or confusing.   Fortunately I discovered a simple and well explained example by Scott Larson here.  This one is a more generalized form of his earlier two input byte interface. That  demonstration which I used for the V1 FPGA Board was quite specific for a SPI device that had a two byte data input and a single byte output. While it works fine for our MCP42010 chip, I wanted to explore a more generalized data I/O FPGA module. The module below modifies Scotts code to have a single byte input and output but one where we can send multiple bytes by controlling separately the chip select (CS*)  line.  For most SPI chips you can send multiple bytes in any time frame you like so long as you keep CS* low.  That is what we do here.  I used Scott's VHDL code.  I actually don't use his "Continuous mode flag" -- this would require a FIFO to keep the module input fed. Instead I simply control CS* separately keeping it low until all bytes are sent.   It took me a while to realize this approach. A big help was an article by Ben Martin using an Arduino chip to program a SPI EPROM. See here.  This program was invaluable because it allowed me to write known data to the SPI PROM and check it read back correctly.

As an example we will read and write to a 128K SPI Serial 128k AT25128 Atmel EEPROM.
This is a 8 pin chip with the following pinout.
Remember unlike the equivalent I2C chip there is a separate data in and out pin. Chip/device addressing is done in the software. To program the chip WP* must be brought high. This chip in fact has an internal addressable control register which allows you to write protect pages of the chip as well as other things.  We will not use that facility in this simple example.
Here is the core FPGA module.
You will note that now the module now has single 8 bit input and output lines.  Gone are the separate two 8 bit "
CMD" and "Data" bus input lines.  It is not as easy to write data to the PROM from the Z80 Monitor commands one by one.  So load the I2C.COM file into RAM at 100H using a program like XModem and the 'X' command of the Z80 Master monitor and run it at 100H.

Dip_SW1 & Dip_SW2 to Closed Position. (SW2 does not seem to matter, but SW1 (CPOL) must be Close,  i.e. FPGA_G11 LOW).
If set correctly the LEDs L2 & L3 on the WaveShare Board should be on.

Here is a picture of the setup:
    Live board
This circuit is one of the components of the FPGA Board Shield #1 board.
Here is a picture of the signon menu running at 100H in RAM.
Here is the core SPI write Z80 code.

BYTE_WRITE:                       ; >>>>>>>>>>>>>>>>>>> START CORE BYTE WRITE ROUTINE >>>>>>>>>>>>>>>>>>
      LD    A,EEPROM_CS_LOW       ; ADDRESS IN (START_STORE), Data in (DATA_STORE), NOTE HL++ on return
      OUT   (SPI_CS),A            ; Select EEPROM
      LD    A,INST_WREN           ; Set Enable WR latch
      OUT   (SPI_DATA_OUT),A
WRITE_SPI             ; Send Write pulse (Port 6B)
      LD    A,EEPROM_CS_HIGH      ; Deselect EEPROM
      OUT   (SPI_CS),A
      LD    A,EEPROM_CS_LOW       ; Select EEPROM leave low (Seems to be required each time)
      OUT   (SPI_CS),A
      LD    A,INST_WRITE          ; Read EEPROM instruction
      OUT   (SPI_DATA_OUT),A
WRITE_SPI             ; Send Write pulse (Port 6B)
      LD    HL,(START_STORE)      ; Store address in PROM HL
      LD    A,H
      OUT   (SPI_DATA_OUT),A
WRITE_SPI             ; Send Write pulse
      LD    A,L
      OUT   (SPI_DATA_OUT),A
WRITE_SPI             ; Send Write pulse
      LD    A,(DATA_STORE)        ; Get Data Byte
      OUT   (SPI_DATA_OUT),A
WRITE_SPI             ; Send Write pulse
      LD    A,EEPROM_CS_HIGH      ; Deselect EEPROM
      OUT   (SPI_CS),A
      INC   HL                    ; NOTE [HL] is incremented in BYTE_WRITE on return
      LD    (START_STORE),HL
      LD    A,EEPROM_CS_HIGH      ; Deselect EEPROM
      OUT   (SPI_CS),A

      IN     A,(SPI_STATUS)       ; Wait until busy is low
      BIT    0,A
      JR     NZ,WRITE_SPI
      OUT    (SPI_WRITE),A        ; Send Write pulse (Port 6B)
      PUSH   HL
      LD     HL,100
      DEC    HL                   ; Allow time for Busy to rise
      LD     A,H
      OR     A,L
      JR     NZ,DELAY
      IN     A,(SPI_STATUS)       ; Wait for busy to go back low
      BIT    0,A
      JR     NZ,WAIT1
      POP    HL

One thing to keep in mind with this EEPROM is after you write a byte you must allow the chip some time to do so. You will see a call to "DELAYX:" within the Z80 code to do this. The SPI chip actully has a status register that can be read to determine if it is busy writing (see the datasheet). However for most purposes we can just insert an overkill time delay.

No bugs reported to data.

AT25128 Atmel EEPROM        
    (V1.0 2/23/2019)  

  (V1.0 2/23/2019)

Other pages describing my S-100 hardware and software.
Please click here to continue...

This page was last modified on 03/24/2019