FPGA Discovery Protocol

1 Introduction

The FPGA Service Discovery Protocol, FPGA-disc, is a protocol to discover and expose services using FPGAs and other devices connected to a network.
The service typically consists of a port that can be accessed by TCP or UDP.

The protocol is designed to be robust and easily implemented using hardware description languages, HDL, e.g. VHDL or Verilog.

The protocol is using UDP broadcast or UDP unicast for service requests and UDP unicast for reply.

The requests are always sent to port PORTNUM.

At startup the service provider can broadcast a network entry message. This message is the same as the reply message except that it is broadcasted.

The unique service identifier consists of a Service Provider Id and a Service Id.

1.1 Service Provider Id, SPId


The Service Provider Id, SPId, is a unique unsigned 16 bit integer value. The Service Providers are administered by FPGA-Cores.

The FPGA-Cores IP-cores are typically built with a fixed SPId. The cores can contain services from both customer and FPGA-Cores. E.g. flash programming/boot and ILA debug port will have FPGA-Cores as Service Provider.

For example, the following Service Providers are defined:
0xFC01 FPGA Cores
0x0001 Non Commercial Service Providers
0xFF01 Commercial non unique Service Provider

All freely downloadable cores at FPGA-cores.com will have the “Non Commercial Service Providers” id.

1.2 Service Id, SId


The Service Id is an unsigned 16 bit integer value, administered by the Service Provider and can freely be selected by the Service Provider.

2 Messages

The following types are used in the protocol:

U8 – unsigned 8 bit integer
U16 – unsigned 16 bit integer
U32 – unsigned 32 bit integer

Most significant byte is sent first.

2.1 Discover Services – 0x56


This message is sent by all network hosts that want to find services that are provided. The message is sent using UDP. Messages can be broadcasted, multi-casted or uni-casted.

The message has the following structure:

U8 Discover Command: 0x56
U16 option id 1 – optional
U32 options 1
U16 option id 2
U32 options 2

The message length is 1 + n*6 Bytes, where n is number of options.

2.1.1 Options


All hosts MUST be able to handle packets with options. Options that are not supported are safely ignored.

The following options are currently defined.

2.1.1.1 Request Service, Option Id=0x8001

Use this option to minimize network traffic. Only services that match requested service are returned.
Request specific service
Option ID: 0x8001
Option Data :
U16 Provider ID
U16 Service ID

If service ID = 0x0000 then all services that match Provider ID will be returned.

If service provider does not support this option then all services will be returned.

2.2 Service Information – 0x55


This UDP message is sent by clients as response to “Discover Services”. The message is sent to the requester’s source port.

The message can also be broadcasted or multicasted when the host enters the network. These broadcast/multicast messages are sent to PORTNUM.

The message has the following structure:

U8 command 0x55
U8 status
U16 service provider id
U16 service
U16 port
U16 option id 1 – optional
U32 options 1
U16 option id 2
U32 options 2

Thus the message length 8 + n * 6, where n is number of options.

2.2.1 Options


There are currently no options defined.

All hosts MUST be able to handle packets with options. Options that are not supported are safely ignored.

Usage Example c++


In the following example all public services on the local network is reported to the standard out, cout.

void ListServices()
{
    int sock;
    sockaddr_in remoteAddr;

    // Create a UDP socket
    sock = socket(AF_INET, SOCK_DGRAM, 0);

    // Enable UDP broadcast
    int enabled = 1;
    if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, (char*)&enabled, sizeof(&enabled)) < 0)
    {
        return;
    }

    // Set UDP address/port
    remoteAddr.sin_family = AF_INET;
    remoteAddr.sin_port = htons(PORTNUM);  // Service Port
    remoteAddr.sin_addr.s_addr = htonl(INADDR_BROADCAST);

    char msg[] = { 0x56, 0x00 };

    if (sendto(sock, (const char*)msg, 1, 0, (sockaddr *)&remoteAddr, sizeof(remoteAddr)) < 0) {
        return;
    }

    Sleep(1000);

    unsigned char RecvBuf[1024];
    int BufLen = 1024;
    sockaddr SenderAddr;
    socklen_t SenderAddrSize = sizeof(SenderAddr);

    int iResult = recvfrom(sock, (char*)RecvBuf, BufLen, 0, &SenderAddr, &SenderAddrSize);
    while (iResult  > 0) {
        if ((RecvBuf[0] == 0x57) && (iResult >= 8)) {

            unsigned char status = RecvBuf[1];
            unsigned int Provider = (RecvBuf[2] << 8) | RecvBuf[3];
            unsigned int Service = (RecvBuf[4] << 8) | RecvBuf[5];
            unsigned short port = (unsigned short)((RecvBuf[6] << 8) | RecvBuf[7]);
           
            char iAddr[64];
            inet_ntop(AF_INET, &(((sockaddr_in*)&SenderAddr)->sin_addr), iAddr, 64);
           
            cout << "Service Provider:" << Provider
                << " Service:" << Service
                << " Port:" << port  
                << " IPAdd:" << iAddr << endl;
        }
        iResult = recvfrom(sock, (char*)RecvBuf, BufLen, 0, &SenderAddr, &SenderAddrSize);
    }

}

Connect your FPGA