Jump to Navigation

Java API

The software interface is provided by bindings for C/C++Python, 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 Java bindings are used by including the riffa.jar file in the classpath for compiling and running. You will need Java 1.4 or greater. Below is a complete example and an API listing.
  1. import edu.ucsd.cs.riffa.*;
  2. import java.nio.ByteBuffer;
  3.  
  4. public class MyApp {
  5. private static final int BUF_SIZE = 1*1024*1024;
  6.  
  7. public void main(String[] args) throws Exception {
  8. int fid = 0;
  9. int channel = 0;
  10.  
  11. ByteBuffer buf = ByteBuffer.allocateDirect(BUF_SIZE);
  12. Fpga fpga = Fpga.open(fid);
  13. fpga.send(channel, buf, BUF_SIZE, 0, true, 0L);
  14. fpga.recv(channel, buf, BUF_SIZE, 0L);
  15. fpga.close();
  16. }
  17. }

 

This example first opens up the FPGA with id 0. It then sends 4 MB of data (1 mega-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 4 MB, with 0 timeout. 
 
Note a few things:
  1. We're using a java.nio.ByteBuffer, not a byte[]. ByteBuffer's expose the underlying byte[], which makes it easy for reading/writing. They also have methods to return different java.nio.Buffer subclasses for different primitives (e.g. java.nio.IntBuffer for int data). These methods do not copy the underlying data, they simply reinterpret it as an array of the specified primitive type. The different java.nio.Buffer subclasses provide methods to access the underlying data as an array of primitives (e.g. int[]). Avoiding unnecessary copying is crucial for maintaining high performance.
  2. We're using the same buffer to send data and receive data. This is not required, it just makes for a simpler example. 
  3. 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. 
  4. 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.
One more important note. Java uses network byte order, which is big endian. Most workstations are little endian (i.e. Intel or AMD). VHDL and Verilog are also little endian in that the left most bit is always the most significant. The ByteBuffer classes will encode/decode numeric values in big endian format. This encoding only applies to data in the ByteBuffer. Java primitives are handled correctly without any need for byte swapping. For example:
  1. ByteBuffer buf = ByteBuffer.allocateDirect(4);
  2. IntBuffer ibuf = buf.asIntBuffer();
  3. ibuf.put(0, 4);
  4. int v = 4;
  5. System.out.printf("%d - 0x%02x%02x%02x%02x\n", ibuf.get(0),
  6. buf.get(3), buf.get(2), buf.get(1), buf.get(0));
  7. System.out.printf("%d - 0x%02x%02x%02x%02x\n", v,
  8. v&;0xFF000000, v&;0xFF0000, v&;0xFF00, v&;0xFF);

Prints the following:

    4 - 0x04000000
    4 - 0x00000004
The first output line is from the ByteBuffer and shows the big endian encoding. The second is from the int and shows the little endian format. You'll only need to consider byte swapping when sending ByteBuffer payload data between the FPGA and the Java program. Byte swapping is most effectively done in hardware on the send and receive data ports.
 
The above example can be compiled as:
    javac -cp riffa.jar MyApp.java
 
and run as:
    java -cp riffa.jar:./ MyApp
 
API
 
edu.ucsd.cs.riffa
 
public class FpgaInfo
Value object to hold information about all the installed FPGA accessible by RIFFA.
 

public int getNumFpgas()
 
Returns: the number of RIFFA accessible FPGAs installed in the system.
 
Returns:
Number of RIFFA accessible FPGAs installed in the system.

public int getId(int pos)
 
Returns the FPGA id at position pos. This id is used to open the FPGA on the Fpga's open method.
 
Returns:
FPGA id at position pos.

public int getNumChannels(int pos)
 
Returns the number of RIFFA channels configured on the FPGA at position pos.
 
Returns:
Number of RIFFA channels configured on the FPGA at position pos.

public String getName(int pos)
 
Returns the name of the FPGA at position pos. This is typically the PCIe bus and slot number.
 
Returns:
Name of the FPGA at position pos.

public int getVendorId(int pos)
 
Returns the FPGA vendor id at position pos.
 
Returns:
The FPGA vendor id at position pos.

public int getDeviceId(int pos)
 
Returns the FPGA device id at position pos.
 
Returns:
The FPGA device id at position pos.

public class Fpga
 
Represents a FPGA accessible by RIFFA. The usage pattern is:
    Fpga f = Fpga.open(...);
    f.send(...);
    f.recv(...);
    f.close(...);
 
The static method list can be used to get a listing of the FPGAs installed in the system and their ids. You'll need the FPGA id to pass to the open method. If only 1 FPGA is installed in the system, it's id will always be 0.
 
In the send and recv methods below use java.nio.ByteBuffer instead of a byte[] to represent the data for sending and buffer for receiving data. The java.nio.ByteBuffer class is used because the underlying byte[] can easily be accessed. But more importantly, it has methods to reinterpret the underlying byte[] into other primitive array types (e.g. int[]) without copying the contents of the array. Copying arrays (especially large arrays) will reduce throughput considerably and is best to be avoided. Use the allocateDirect method on ByteBuffer to create a new instance. Then use one of the asXXXBufer methods to acquire the appropriate java.nio.Buffer subclass. The example below illustrates this:
    java.nio.ByteBuffer bb = java.nio.ByteBuffer.allocateDirect(NUM_INTS*4);
    java.nio.IntBuffer ib = bb.asIntBuffer();
    for (i = 0; i < NUM_INTS; i++)
        ib.put(i, ...)
    Fpga f = Fpga.open(0);
    fpga.send(0, bb, NUM_INTS, 0, true, 0L);
 

 
public static FpgaInfo list()
 
Populates and returns a FpgaInfo object with all FPGAs registered in the system. Returns a FpgaInfo object on success. Returns null on error.
 
Returns:
FpgaInfo object on success or null.
 
 

public static Fpga open(int id)
 
Initializes the FPGA specified by id. On success, returns a Fpga object. On error, returns null. Each FPGA must be opened before any channels can be accessed. Once opened, any number of threads can use the Fpga object.
 
id - Identifier for the FPGA (in single FPGA installations, this is always 0).
 
Returns:
Fpga object or null.
 

public void close()
 
Cleans up memory/resources for the FPGA represented by this instance.
 
Returns:
Nothing.

public int send(int chnl, java.nio.ByteBuffer data, int len, int destoff, boolean last, long timeout)
 
Sends len words (4 byte words) from data to FPGA channel chnl on the FPGA represented by this Fpga object. The FPGA channel will be sent lendestoff, and last. The java.nio.ByteBuffer's position and limit are not read (or modified). To send a subset of data from the buffer, set the position and use the slice method to acquire a java.nio.ByteBuffer object that starts at the correct location. The value of destoff is used to support sending 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
 
chnl - Channel number over which to communicate.
data - java.nio.ByteBuffer holding the byte[] to send. Note that the data transfer unit is a 32 bit word.
len - Length of data to send, in (32 bit) words. Thus a value of 4 means send 16 bytes.
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.

public int recv(int chnl, java.nio.ByteBuffer data, long timeout)
 
Receives data from the FPGA channel chnl to the java.nio.ByteBuffer object, on the FPGA represented by this Fpga object. The FPGA channel can send any amount of data, so the java.nio.ByteBuffer should 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. The java.nio.ByteBuffer's position and limit are not modified. 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 java.nio.ByteBuffer.
 
chnl - Channel number over which to communicate.
data - java.nio.ByteBuffer into which received data will be written.
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 java.nio.ByteBuffer.

public void reset()
 
Resets the state of the FPGA and 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.
 
Returns:
Nothing.
 
 
 


Main menu 2

Page | by Dr. Radut