Getting Started With FPGA

FTDI Asynchronous FIFO Interfacing with Neso, Skoll and Styx

2176 views March 23, 2018 rohitsingh 6

Introduction:

The FTDI FT2232H USB 2.0 chip on-board Neso, Skoll and Styx provides users with a FIFO interface for data transfer between FPGA and host PC. The FT2232H chip features two channels or ports, which can be configured into various modes like UART, FIFO, JTAG etc. On Neso and Skoll, channel A is used for FPGA configuration and flash programming, whereas Channel B is available for custom usage. On Styx, Channel B is used for FPGA configuration and flash programming, whereas channel A is for custom usage. This article demonstrates communication between host and the board using Asynchronous FIFO mode via boards’ respective channels of FT2232H. The Channel B of FT2232H in case of Neso and Skoll (Channel A for Styx), when configured as FIFO, acts as a USB-FIFO bridge between host and FPGA. This article is applicable for Neso Artix 7 FPGA ModuleSkoll Kintex 7 FPGA Module as well as Styx Xilinx Xynq FPGA Module. You can follow this article with any of the three boards. Let’s get started!

Prerequisites:

  • Hardware:
    • Neso or Skoll or Styx
    • Xilinx Platform Cable USB II (optional)
  • Software:
    • Vivado 2017.3 or later
    • python 3+

Step 1:

Download and install Vivado Board Support Package files for your respective board from here. Follow the README.md file on how to install Vivado Board Support Package files for Numato Lab boards.

Step 2:

Open Vivado and select ‘Create Project’ from the Quick Start Section.

Step 3:

Click ‘Next’. In Project Name tab, type in a project name and select a convenient project location.

Step 4:

Click Next without making any changes till you see the ‘Default Part’ tab and select your board from ‘Boards’ option as shown in the image below. If the boards are not listed, make sure board support files are installed correctly.

Step 5:

Right-Click ‘Design Sources’ and select ‘Add Sources’. It will open a configuration window for adding Verilog files.

Step 6:

Download and extract the RTL source files from here. Click ‘Add Files’, select all files and add them to the project.

Here, FTDI’s Asynchronous FIFO interface is used to perform data transfer between FPGA and host PC. As the operation is asynchronous, the data transfers is not synchronous to any common clock between FPGA and FTDI. The design is simpler but has relatively lower speed than synchronous FIFO interface. To use the asynchronous FIFO transfer mode available on FT2232H, its hardware and driver must be configured as 245 FIFO and D2XX respectively. In this article, Channel B of FT2232H (Channel A for Styx) is configured as FIFO, which provides the following signals: 

data: It is bi-directional bus carrying the 8-bit FIFO data. 

rxf_n: It is an active low signal. When de-asserted, does not read data from FIFO. When it is enabled, it indicates that there is some data available in FIFO, the data is read after asserting rd_n. 

txe_n: It is an active low signal. When de-asserted, does not write data to FIFO. When it is asserted, data can be written to FIFO if wr_n is asserted. 

rd_n: It is an active low signal. When asserted, data is driven onto ‘data’ bus from FIFO. 

wr_n: It is an active low signal. When asserted, data from ‘data’ bus is driven into FIFO. 

In the RTL code, FSM is implemented which checks if there is some data available for reading (indicated by pending_read), then the data is read by enabling rd_n signal. When there is some data to be written (indicated by pending_write), wr_n signal is asserted and incoming data is driven into FIFO.

Step 7:

Right-Click Constraints and select ‘Add sources’.

Step 8:

Click Next and then ‘Create File’. Enter a name for the constraints file and Click Finish.

Step 9:

Copy the following constraints to the newly created .xdc file as per your board,

Neso Constraints:

set_property -dict { PACKAGE_PIN "K16"    IOSTANDARD LVCMOS33 } [get_ports {txe_n}]
set_property -dict { PACKAGE_PIN "G13"    IOSTANDARD LVCMOS33 } [get_ports {rxf_n}]
set_property -dict { PACKAGE_PIN "M13"    IOSTANDARD LVCMOS33 } [get_ports {wr_n}]
set_property -dict { PACKAGE_PIN "D9"     IOSTANDARD LVCMOS33 } [get_ports {rd_n}]
set_property -dict { PACKAGE_PIN "J18"    IOSTANDARD LVCMOS33 } [get_ports {data[7]}]
set_property -dict { PACKAGE_PIN "J17"    IOSTANDARD LVCMOS33 } [get_ports {data[6]}]
set_property -dict { PACKAGE_PIN "G18"    IOSTANDARD LVCMOS33 } [get_ports {data[5]}]
set_property -dict { PACKAGE_PIN "F18"    IOSTANDARD LVCMOS33 } [get_ports {data[4]}]
set_property -dict { PACKAGE_PIN "E18"    IOSTANDARD LVCMOS33 } [get_ports {data[3]}]
set_property -dict { PACKAGE_PIN "D18"    IOSTANDARD LVCMOS33 } [get_ports {data[2]}]
set_property -dict { PACKAGE_PIN "B18"    IOSTANDARD LVCMOS33 } [get_ports {data[1]}]
set_property -dict { PACKAGE_PIN "A18"    IOSTANDARD LVCMOS33 } [get_ports {data[0]}]
set_property -dict { PACKAGE_PIN "F4"     IOSTANDARD LVCMOS33     SLEW FAST } [get_ports {clk}]
set_property -dict { PACKAGE_PIN "A14"    IOSTANDARD LVCMOS33     SLEW FAST    PULLUP TRUE } [get_ports {rst}]

Skoll Constraints:

set_property -dict { PACKAGE_PIN "Y22"    IOSTANDARD LVCMOS33 } [get_ports {data[7]}]; 
set_property -dict { PACKAGE_PIN "Y21"    IOSTANDARD LVCMOS33 } [get_ports {data[6]}]; 
set_property -dict { PACKAGE_PIN "W22"    IOSTANDARD LVCMOS33 } [get_ports {data[5]}]; 
set_property -dict { PACKAGE_PIN "W21"    IOSTANDARD LVCMOS33 } [get_ports {data[4]}]; 
set_property -dict { PACKAGE_PIN "V22"    IOSTANDARD LVCMOS33 } [get_ports {data[3]}]; 
set_property -dict { PACKAGE_PIN "U21"    IOSTANDARD LVCMOS33 } [get_ports {data[2]}]; 
set_property -dict { PACKAGE_PIN "U22"    IOSTANDARD LVCMOS33 } [get_ports {data[1]}];
set_property -dict { PACKAGE_PIN "T21"    IOSTANDARD LVCMOS33 } [get_ports {data[0]}]; 
set_property -dict { PACKAGE_PIN "H15"    IOSTANDARD LVCMOS33 } [get_ports {txe_n}]; 
set_property -dict { PACKAGE_PIN "D12"    IOSTANDARD LVCMOS33 } [get_ports {rxf_n}];
set_property -dict { PACKAGE_PIN "V14"    IOSTANDARD LVCMOS33 } [get_ports {wr_n}];
set_property -dict { PACKAGE_PIN "K17"    IOSTANDARD LVCMOS33 } [get_ports {rd_n}];
set_property -dict { PACKAGE_PIN "E11"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {clk}];
set_property -dict { PACKAGE_PIN "K22"    IOSTANDARD LVCMOS33    SLEW FAST    PULLUP TRUE } [get_ports {rst}];

Styx Constraints:

set_property -dict { PACKAGE_PIN "T22"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[0]}];
set_property -dict { PACKAGE_PIN "T21"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[1]}]; 
set_property -dict { PACKAGE_PIN "U22"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[2]}]; 
set_property -dict { PACKAGE_PIN "U21"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[3]}]; 
set_property -dict { PACKAGE_PIN "V22"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[4]}]; 
set_property -dict { PACKAGE_PIN "W22"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[5]}]; 
set_property -dict { PACKAGE_PIN "AA22"   IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[6]}]; 
set_property -dict { PACKAGE_PIN "AB22"   IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {data[7]}]; 
set_property -dict { PACKAGE_PIN "W20"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {rxf_n}] ;
set_property -dict { PACKAGE_PIN "Y21"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {txe_n}] ; 
set_property -dict { PACKAGE_PIN "Y20"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {rd_n}] ; 
set_property -dict { PACKAGE_PIN "W21"    IOSTANDARD LVCMOS33    SLEW FAST } [get_ports {wr_n}] ;
set_property -dict { PACKAGE_PIN "M19"    IOSTANDARD LVCMOS33    SLEW FAST    PULLUP TRUE } [get_ports {rst}] ;
set_property -dict { PACKAGE_PIN "Y6"     IOSTANDARD LVCMOS33 } [get_ports {clk}];

Step 10:

In Project Manager Tab, Click ‘Run Synthesis’. If Synthesis is successful, Click ‘Run Implementation’ and on successful implementation select ‘Generate Bitstream’.

Step 11:

After Successful generation of bitstream, select “Open Hardware Manager” and Click OK.

Step 12:

Click ‘Open target’ and ‘Auto Connect’. It will connect to the board. Make sure you have connected the Board to JTAG, Power supply and USB.

Step 13:

Click ‘Program Device’. Dialog window for programming will open. Program the bit file by clicking ‘Program’.

Step 14:

Once the device is programmed, program proper FTDI Settings for FIFO to work.

For Neso v1 and Skoll v1:

Change FT Prog settings as follows: Go to Device:0 -> Hardware Specific -> Port B -> Hardware and select RS245 FIFO. Now goto Hardware Specific -> Port B -> Driver and select D2XX.

For Neso v2, Skoll v2 and Styx:

Change FT Prog settings as follows: Go to Device:0 -> Hardware Specific -> Port A -> Hardware and select RS245 FIFO. Now go to Hardware Specific -> Port A -> Driver and select D2XX.

Step 15:

To check if Asynchronous FIFO is working as expected, write some data through FIFO, read it back and compare it. If the data written matches the data that is read then it means that the design is working fine.

To check this, copy the following python code in text editor and save it as ‘async_test.py’

from __future__ import print_function

import time
import random

import ftd2xx

# Change device name in the next line according to your board
# For Neso 'Neso Artix 7 FPGA Module A' (Replace A with B for Neso v1)
# For Skoll 'Skoll Kintex 7 FPGA Module A' (Replace A with B for Skoll v1)
# For Styx 'Styx Zynq FPGA Module A'
dev = ftd2xx.openEx(b'Neso Artix 7 FPGA Module B', 2) 
dev.setTimeouts(5000, 5000)
dev.purge(ftd2xx.defines.PURGE_TX|ftd2xx.defines.PURGE_RX)
print("\nDevice Details :")
print("Serial : " , dev.getDeviceInfo()['serial'])
print("Type : " , dev.getDeviceInfo()['type'])
print("ID : " , dev.getDeviceInfo()['id'])
print("Description : " , dev.getDeviceInfo()['description'])

BLOCK_LEN = 2048 * 10

tx_data = bytes(bytearray([ random.randrange(0, 256) for i in range(BLOCK_LEN) ]))
print("\nWriting %d KB of data to the deivce..." % (BLOCK_LEN / 1024))
ts = time.time()
written = dev.write(tx_data)

rx_data = dev.read(BLOCK_LEN)
te = time.time()
print("\nReading %d KB of data from the deivce..." % (BLOCK_LEN / 1024))
p = te - ts

print("\nComparing data...\n")
if (tx_data == rx_data):
 print("Data written matches the data read\n")
else:
 print("Data verification failed\n") 
 
dev.close()
print("Transfer Time = %.3f seconds" % p)
speed = (BLOCK_LEN / 1024./1024 / p)
print("Transfer Rate = %.3f MB/s" % speed)

print("\nAsynchronous Test Finished Successfully!\nThank You!!")

Open command prompt. Change to the directory of the file async_test.py and enter the following command:

python async_test.py

Neso Output:

Skoll Output:

Styx Output:

Was this helpful?

Leave A Comment
*
*