Jump to Navigation

Python API

The software interface is provided by bindings for C/C++, Python, Matlab and Java. After installation all bindings are available in their respective runtime environments. The API is based on the notion of channels. RIFFA 2 can be configured to support between 1 - 12 independent channels. Each channel connects to an IP core and can be addressed by specifying the channel number from the user application. The channels are independent and thread safe. At most one thread should be used to access a single channel.

The Python bindings are used by importing the riffa module. You will need Python 2.7 or greater. Below are a couple of complete examples and an API listing.
  1. import riffa
  2. import array
  3.  
  4. fid = 0
  5. channel = 0
  6.  
  7. data = array.array('I', range(100))
  8. fd = riffa.fpga_open(fid)
  9. riffa.fpga_send(fd, channel, data, 100, 0, True, 0)
  10. riffa.fpga_recv(fd, channel, data, 0)
  11. riffa.fpga_close(fd)

This example first opens up FPGA with id 0. It then sends 400 bytes of data (100 4-byte words) to channel 0 with 0 destination offset, 0 timeout, and marks the transfer as last. The IP core on channel 0 is designed to send back some data. So the next call is to receive data from the same channel, up to 400 bytes (that's the size of the data array), with 0 timeout. 

Note a few things:
  1. We're using a array to hold our data. The array type supports the Python buffer protocol interface which means a memoryview object can be created from it*. A memoryview object exposes the underlying memory of the object implementing the buffer protocol interface. This memory is read from and written to directly without copying. Avoiding this copying preserves high performance. Therefore, the fpga_send and fpga_recv functions only accept data objects that support a memoryview. Python list and tuple do not support the buffer protocol interface. So you'll want to use arraynumpy array or other types that do. 
  2. The length of words sent is independent of the size of the supplied data object or the size of each element in the data object. This is meant to force the programmer to think about how many bytes each element uses in the data object because the IP core designer will need to know how many and how to interpret that data. 
  3. We're using the same buffer to send data and receive data. This is not required, it just makes for a simpler example. 
  4. The timeout is set to 0. If there's a problem with the IP core logic, a 0 timeout will cause the program to wait forever. 
  5. In practice, you'd want to check the return values to see how much data was sent and received. You'd also probably want some error handling.
  1. import riffa
  2. import numpy
  3.  
  4. fid = 0
  5. channel = 0
  6.  
  7. data = numpy.array(range(100))
  8. fd = riffa.fpga_open(fid)
  9. riffa.fpga_send(fd, channel, data, 200, 0, True, 0)
  10. riffa.fpga_recv(fd, channel, data, 0)
  11. riffa.fpga_close(fd)

This example is nearly identical to the previous except that it uses a numpy array. Since the numpy array stores values using 8 bytes per element, we changed the length value to accommodate. 

 
* The Python buffer protocol interface is a Python 3 design, however it has been back ported to Python 2.7. So using version 2.7 should be fine. However, the Python array type was not completely implemented in 2.7. As a result you cannot create a memoryview on a Python array in 2.7. To support this type, we have exploited another method to support Python array types as data arguments in Python 2.7. 

riffa.fpga_list()
 
Populates and returns a FpgaInfoList object with information on all FPGAs registered in the system or None on error. Print the FpgaInfoList object to see the information.
 
Returns:
A FpgaInfoList object on success, None on error.
 
 

riffa.fpga_open(id)
 
Initializes the FPGA specified by id. On success, returns an integer descriptor for the FPGA. On error, returns None. Each FPGA must be opened before any channels can be accessed. Once opened, any number of threads can use the descriptor.
 
id - Identifier for the FPGA (in single FPGA installations, this is always 0).
 
Returns:
An integer descriptor or None.
 

riffa.fpga_close(fd)
 
Cleans up memory/resources for the FPGA specified by the fd descriptor.
 
fd - FPGA descriptor.
 
Returns:
Nothing.

riffa.fpga_send(fd, chnl, data, length, destoff, last, timeout)
 
Sends length words (4 byte words) from data to FPGA channel chnl using the fd descriptor. The data object must implement the Python buffer protocol or an exception will be raised. Note that Python array and numpy array both implement the protocol. The FPGA channel will be sent lengthdestoff, and last. You can use Python array slicing (e.g. [m:n] syntax) to create a new array object to send a subset of data. Array slicing will not copy the data on Python buffer protocol capable objects. The value of destoff is used to support sending of data across multiple send transactions. Note that only the low 31 bits of this value are sent. If last is True, the channel should interpret the end of this send as the end of a transaction. If last is False, the channel should wait for additional sends before the end of the transaction. If timeout is non-zero, this call will send data and wait up to timeout ms for the FPGA to respond (between packets) before timing out. If timeout is zero, this call may block indefinitely. Multiple threads sending on the same channel may result in corrupt data or error. This function is thread safe across channels. Returns the number of words sent. 
 
fd - FPGA descriptor.
chnl - Channel number over which to communicate (0-11).
data - Object that implements the Python buffer protocol (i.e. can create a memoryview from the object).
length - Length of data to send, in (32 bit) words. Thus a value of 4 means send 16 bytes. Not necessarily the number of elements from data to send.
destoff - Value sent to FPGA core to indicate where to start writing this data. Only the least significant 31 bits are sent (not all 32).
last - If True, this transfer is the last in a sequence of transfers. If False, this transfer is not the last in a sequence of transfers (more transfers to come).
timeout - Timeout value in ms. If 0, no timeout is specified. Otherwise, the PC will wait up to timeout ms in between PC/FPGA communications.
 
 
 
Returns:
The number of words sent.

riffa.fpga_recv(fd,chnl,data,timeout);
 
Receives data from the FPGA channel chnl to the data object, using the fd descriptor. Just as with the fpga_send function, the data object must implement the Python buffer protocol or an exception will be raised. The FPGA channel can send any amount of data, so the data array must be large enough to accommodate. The FPGA will specify an offset value which will determine where received data will start being written. If the amount of data plus offset exceed the size of the data array, then the additional data will be discarded. If timeout is non-zero, this call will wait up to timeout ms for the FPGA to respond (between packets) before timing out. If timeout is zero, this call may block indefinitely. Multiple threads receiving on the same channel may result in corrupt data or error. This function is thread safe across channels. Returns the number of words received to the data array. 
 
fd - FPGA descriptor.
chnl - Channel number over which to communicate (0-11).
data - Object that implements the Python buffer protocol (i.e. can create a memoryview from the object).
timeout - Timeout value in ms. If 0, no timeout is specified. Otherwise, the PC will wait up to timeout ms in between PC/FPGA communications.
 
Returns:
The number of words received to the data array.

riffa.fpga_reset(fd);
 
Resets the state of the FPGA and all transfers across all channels. This is meant to be used as an alternative to rebooting if an error occurs while sending/receiving. Calling this function while other threads are sending or receiving will result in unexpected behavior.
 
fd - FPGA descriptor.
 
Returns:
Nothing.

 



Main menu 2

Page | by Dr. Radut