Skip to content
Snippets Groups Projects
etherbone.h 24.79 KiB
/** @file etherbone.h
 *  @brief The public API of the Etherbone library.
 *
 *  Copyright (C) 2011-2012 GSI Helmholtz Centre for Heavy Ion Research GmbH 
 *
 *  All Etherbone object types are opaque in this interface.
 *  Only those methods listed in this header comprise the public interface.
 *
 *  @author Wesley W. Terpstra <w.terpstra@gsi.de>
 *
 *  @bug None!
 *
 *******************************************************************************
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU Lesser General Public
 *  License as published by the Free Software Foundation; either
 *  version 3 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Lesser General Public License for more details.
 *  
 *  You should have received a copy of the GNU Lesser General Public
 *  License along with this library. If not, see <http://www.gnu.org/licenses/>.
 *******************************************************************************
 */

#ifndef ETHERBONE_H
#define ETHERBONE_H

#define EB_PROTOCOL_VERSION	1
#define EB_ABI_VERSION		0x01	/* incremented on incompatible changes */

#include <stdint.h>   /* uint32_t ... */
#include <inttypes.h> /* EB_DATA_FMT ... */

/* Symbol visibility definitions */
#ifdef __WIN32
#ifdef ETHERBONE_IMPL
#define EB_PUBLIC __declspec(dllexport)
#define EB_PRIVATE
#else
#define EB_PUBLIC __declspec(dllimport)
#define EB_PRIVATE
#endif
#else
#define EB_PUBLIC
#define EB_PRIVATE __attribute__((visibility("hidden")))
#endif

/* Pointer type -- depends on memory implementation */
#ifdef EB_USE_MALLOC
#define EB_POINTER(typ) struct typ*
#define EB_NULL 0
#define EB_MEMORY_MODEL 0x0001U
#else
#define EB_POINTER(typ) uint16_t
#define EB_NULL ((uint16_t)-1)
#define EB_MEMORY_MODEL 0x0000U
#endif

/* Opaque structural types */
typedef EB_POINTER(eb_socket)    eb_socket_t;
typedef EB_POINTER(eb_device)    eb_device_t;
typedef EB_POINTER(eb_cycle)     eb_cycle_t;
typedef EB_POINTER(eb_operation) eb_operation_t;

/* Configurable maximum bus width supported */
#if defined(EB_FORCE_64)
typedef uint64_t eb_address_t;
typedef uint64_t eb_data_t;
#define EB_ADDR_FMT PRIx64
#define EB_DATA_FMT PRIx64
#define EB_DATA_C UINT64_C
#define EB_ADDR_C UINT64_C
#elif defined(EB_FORCE_32)
typedef uint32_t eb_address_t;
typedef uint32_t eb_data_t;
#define EB_ADDR_FMT PRIx32
#define EB_DATA_FMT PRIx32
#define EB_DATA_C UINT32_C
#define EB_ADDR_C UINT32_C
#elif defined(EB_FORCE_16)
typedef uint16_t eb_address_t;
typedef uint16_t eb_data_t;
#define EB_ADDR_FMT PRIx16
#define EB_DATA_FMT PRIx16
#define EB_DATA_C UINT16_C
#define EB_ADDR_C UINT16_C
#else
/* The default maximum width is the machine word-size */
typedef uintptr_t eb_address_t;
typedef uintptr_t eb_data_t;
#define EB_ADDR_FMT PRIxPTR
#define EB_DATA_FMT PRIxPTR
#define EB_DATA_C UINT64_C
#define EB_ADDR_C UINT64_C
#endif

/* Identify the library ABI this header must match */
#define EB_BUS_MODEL	(0x0010U * sizeof(eb_address_t)) + (0x0001U * sizeof(eb_data_t))
#define EB_ABI_CODE	((EB_ABI_VERSION << 8) + EB_BUS_MODEL + EB_MEMORY_MODEL)

/* Status codes */
typedef int eb_status_t;
#define EB_OK		0
#define EB_FAIL		-1
#define EB_ADDRESS	-2
#define EB_WIDTH	-3
#define EB_OVERFLOW	-4
#define EB_ENDIAN	-5
#define EB_BUSY		-6
#define EB_TIMEOUT	-7
#define EB_OOM          -8
#define EB_ABI		-9

/* A bitmask containing values from EB_DATAX | EB_ADDRX */
typedef uint8_t eb_width_t;
/* A bitmask containing values from EB_DATAX | EB_ENDIAN_MASK */
typedef uint8_t eb_format_t;

#define EB_DATA8	0x01
#define EB_DATA16	0x02
#define EB_DATA32	0x04
#define EB_DATA64	0x08
#define EB_DATAX	0x0f

#define EB_ADDR8	0x10
#define EB_ADDR16	0x20
#define EB_ADDR32	0x40
#define EB_ADDR64	0x80
#define EB_ADDRX	0xf0

#define EB_ENDIAN_MASK	0x30
#define	EB_BIG_ENDIAN	0x10
#define EB_LITTLE_ENDIAN 0x20

/* Callback types */
typedef void *eb_user_data_t;
typedef void (*eb_callback_t )(eb_user_data_t, eb_device_t, eb_operation_t, eb_status_t);
typedef int eb_descriptor_t;
typedef void (*eb_descriptor_callback_t)(eb_user_data_t, eb_descriptor_t);

typedef struct sdwb_bus {
  uint8_t  magic[16];
  uint64_t bus_end;
  uint16_t sdwb_records;
  uint8_t  sdwb_ver_major;
  uint8_t  sdwb_ver_minor;
  uint32_t bus_vendor;
  uint32_t bus_device;
  uint32_t bus_version;
  uint32_t bus_date;
  uint32_t bus_flags;
  uint8_t  description[16]; 
} *sdwb_bus_t;

typedef struct sdwb_device {
  uint64_t wbd_begin;
  uint64_t wbd_end;
  uint64_t sdwb_child;
#define WBD_FLAG_PRESENT	0x01
#define WBD_FLAG_LITTLE_ENDIAN	0x02
#define WBD_FLAG_HAS_CHILD	0x04
  uint8_t  wbd_flags;
  uint8_t  wbd_width;
  uint8_t  abi_ver_major;
  uint8_t  abi_ver_minor;
  uint32_t abi_class;
  uint32_t dev_vendor;
  uint32_t dev_device;
  uint32_t dev_version;
  uint32_t dev_date;
  uint8_t  description[16];
} *sdwb_device_t;

/* Complete bus description */
typedef struct sdwb {
  struct sdwb_bus    bus;
  struct sdwb_device device[1]; /* bus.sdwb_records-1 elements (not 1) */
} *sdwb_t;

/* Handler descriptor */
typedef struct eb_handler {
  /* This pointer must remain valid until after you detach the device */
  sdwb_device_t device;
  
  eb_user_data_t data;
  eb_status_t (*read) (eb_user_data_t, eb_address_t, eb_width_t, eb_data_t*);
  eb_status_t (*write)(eb_user_data_t, eb_address_t, eb_width_t, eb_data_t);
} *eb_handler_t;

#ifdef __cplusplus
extern "C" {
#endif

/****************************************************************************/
/*                                 C99 API                                  */
/****************************************************************************/

/* Convert status to a human-readable printable string */
EB_PUBLIC
const char* eb_status(eb_status_t code);

/* Open an Etherbone socket for communicating with remote devices.
 * The abi_code must be EB_ABI_CODE. This confirms library compatability.
 * The port parameter is optional; 0 lets the operating system choose.
 * After opening the socket, poll must be hooked into an event loop.
 * The addr/port widths apply to virtual slaves on the bus.
 *
 * Return codes:
 *   OK		- successfully open the socket port
 *   FAIL	- operating system forbids access
 *   BUSY	- specified port is in use (only possible if port != 0)
 *   WIDTH      - supported_widths were invalid
 *   OOM        - out of memory
 *   ABI        - library is not compatible with application
 */
EB_PUBLIC
eb_status_t eb_socket_open(uint16_t      abi_code,
                           const char*   port, 
                           eb_width_t    supported_widths,
                           eb_socket_t*  result);

/* Close the Etherbone socket.
 * Any use of the socket after successful close will probably segfault!
 *
 * Return codes:
 *   OK		- successfully closed the socket and freed memory
 *   BUSY	- there are open devices on this socket
 */
EB_PUBLIC
eb_status_t eb_socket_close(eb_socket_t socket);

/* Poll the Etherbone socket for activity.
 * This function must be called regularly to receive incoming packets.
 * The caller must first provide the current timestamp using eb_socket_settime.
 * Either call poll very often or hook a read listener on its descriptors.
 */
EB_PUBLIC
void eb_socket_poll(eb_socket_t socket);

/* Update the current timestamp cache (32-bit unsigned seconds since 1970).
 * This should be done before calls to poll.
 */
EB_PUBLIC
void eb_socket_settime(eb_socket_t socket, uint32_t now);

/* Block until the socket is ready to be polled.
 * This function is useful if your program has no event loop of its own.
 * If timeout_us == 0, return immediately. If timeout_us == -1, wait forever.
 * It returns the time expended while waiting.
 * Internally updates eb_socket_settime after call.
 */
EB_PUBLIC
int eb_socket_block(eb_socket_t socket, int timeout_us);

/* Access the underlying file descriptors of the Etherbone socket.
 * THESE MUST NEVER BE READ, WRITTEN, CLOSED, OR MODIFIED IN ANY WAY!
 * They may be used to watch for read readiness to call eb_socket_poll.
 */
EB_PUBLIC
void eb_socket_descriptor(eb_socket_t socket, eb_user_data_t user, eb_descriptor_callback_t cb); 

/* Access the next timestamp of the next timeout to expire.
 * The caller must first provide the current timestamp using eb_socket_settime.
 * When the returned time has been exceeded, poll should be run.
 */
EB_PUBLIC
uint32_t eb_socket_timeout(eb_socket_t socket);

/* Add a device to the virtual bus.
 * This handler receives all reads and writes to the specified address.
 * The handler structure passed to eb_socket_attach need not be preserved.
 * The sdwb_device MUST be preserved until the device is detached.
 * NOTE: the address range [0x0, 0x4000) is reserved for internal use.
 *
 * Return codes:
 *   OK         - the handler has been installed
 *   OOM        - out of memory
 *   ADDRESS    - the specified address range overlaps an existing device.
 */
EB_PUBLIC
eb_status_t eb_socket_attach(eb_socket_t socket, eb_handler_t handler);

/* Detach the device from the virtual bus.
 *
 * Return codes:
 *   OK         - the devices has be removed
 *   ADDRESS    - there is no device at the specified address.
 */
EB_PUBLIC
eb_status_t eb_socket_detach(eb_socket_t socket, sdwb_device_t device);

/* Open a remote Etherbone device.
 * This resolves the address and performs Etherbone end-point discovery.
 * From the mask of proposed bus address widths, one will be selected.
 * From the mask of proposed bus port    widths, one will be selected.
 * The device is probed every 3 seconds, 'attempts' times
 * The default port is taken as 0xEBD0.
 *
 * Return codes:
 *   OK		- the remote etherbone device is ready
 *   ADDRESS	- the network address could not be parsed
 *   TIMEOUT    - timeout waiting for etherbone response
 *   FAIL       - failure of the transport layer (remote host down?)
 *   WIDTH      - could not negotiate an acceptable data bus width
 *   OOM        - out of memory
 */
EB_PUBLIC
eb_status_t eb_device_open(eb_socket_t           socket, 
                           const char*           address,
                           eb_width_t            proposed_widths,
                           int                   attempts,
                           eb_device_t*          result);


/* Recover the negotiated port and address width of the target device.
 */
EB_PUBLIC
eb_width_t eb_device_width(eb_device_t device);

/* Close a remote Etherbone device.
 * Any inflight or ready-to-send cycles will receive EB_TIMEOUT.
 *
 * Return codes:
 *   OK	        - associated memory has been freed
 *   BUSY       - there are unclosed wishbone cycles on this device
 */
EB_PUBLIC
eb_status_t eb_device_close(eb_device_t device);

/* Access the socket backing this device */
EB_PUBLIC
eb_socket_t eb_device_socket(eb_device_t device);

/* Flush commands queued on the device out the socket.
 *
 * Return codes:
 *   OK		- queued packets have been sent
 *   FAIL	- the device has a broken link
 */
EB_PUBLIC
eb_status_t eb_device_flush(eb_device_t device);

/* Begin a wishbone cycle on the remote device.
 * Read/write operations within a cycle hold the device locked.
 * Read/write operations are executed in the order they are queued.
 * Until the cycle is closed and flushed, the operations are not sent.
 * If there is insufficient memory to begin a cycle, EB_NULL is returned.
 * If the device is being closed, EB_NULL is also returned.
 * 
 * Your callback may be called from: eb_socket_poll/eb_device_flush/eb_device_close.
 * It receives these arguments: (user_data, operations, status)
 * 
 * If status != OK, the cycle was never sent to the remote bus.
 * If status == OK, the cycle was sent.
 *
 * When status == EB_OK, 'operations' report the wishbone ERR flag.
 * When status != EB_OK, 'operations' points to the offending operation.
 *
 * Status codes:
 *   OK		- cycle was executed successfully
 *   ADDRESS    - 1. a specified address exceeded device bus address width
 *                2. the address was not aligned to the operation granularity
 *   WIDTH      - 1. written value exceeded the operation granularity
 *                2. the granularity exceeded the device port width
 *   ENDIAN     - operation format was not word size and no endian was specified
 *   OVERFLOW	- too many operations queued for this cycle (wire limit)
 *   TIMEOUT    - remote system never responded to EB request
 *   FAIL       - remote host violated protocol
 *   OOM        - out of memory while queueing operations to the cycle
 */
EB_PUBLIC
eb_cycle_t eb_cycle_open(eb_device_t    device, 
                         eb_user_data_t user_data,
                         eb_callback_t  cb);

/* End a wishbone cycle.
 * This places the complete cycle at end of the device's send queue.
 * You will probably want to eb_flush_device soon after calling eb_cycle_close.
 */
EB_PUBLIC
void eb_cycle_close(eb_cycle_t cycle);

/* End a wishbone cycle.
 * This places the complete cycle at end of the device's send queue.
 * You will probably want to eb_flush_device soon after calling eb_cycle_close.
 * This method does NOT check individual wishbone operation error status.
 */
EB_PUBLIC
void eb_cycle_close_silently(eb_cycle_t cycle);

/* End a wishbone cycle.
 * The cycle is discarded, freed, and the callback never invoked.
 */
EB_PUBLIC
void eb_cycle_abort(eb_cycle_t cycle);

/* Access the device targetted by this cycle */
EB_PUBLIC
eb_device_t eb_cycle_device(eb_cycle_t cycle);

/* Prepare a wishbone read operation.
 * The given address is read from the remote device.
 * The result is written to the data address.
 * If data == 0, the result can still be accessed via eb_operation_data.
 *
 * The operation size is max {x in format: x <= data_width(device) }.
 * When the size is not the device data width, format must include an endian.
 * Your address must be aligned to the operation size.
 */
EB_PUBLIC
void eb_cycle_read(eb_cycle_t    cycle, 
                   eb_address_t  address,
                   eb_format_t   format,
                   eb_data_t*    data);
EB_PUBLIC
void eb_cycle_read_config(eb_cycle_t    cycle, 
                          eb_address_t  address,
                          eb_format_t   format,
                          eb_data_t*    data);

/* Perform a wishbone write operation.
 * The given address is written on the remote device.
 * 
 * The operation size is max {x in width: x <= data_width(device) }.
 * When the size is not the device data width, format must include an endian.
 * Your address must be aligned to this operation size and the data must fit.
 */
EB_PUBLIC
void eb_cycle_write(eb_cycle_t    cycle,
                    eb_address_t  address,
                    eb_format_t   format,
                    eb_data_t     data);
EB_PUBLIC
void eb_cycle_write_config(eb_cycle_t    cycle,
                           eb_address_t  address,
                           eb_format_t   format,
                           eb_data_t     data);

/* Operation result accessors */

/* The next operation in the list. EB_NULL = end-of-list */
EB_PUBLIC eb_operation_t eb_operation_next(eb_operation_t op);

/* Was this operation a read? 1=read, 0=write */
EB_PUBLIC int eb_operation_is_read(eb_operation_t op);
/* Was this operation onthe config space? 1=config, 0=wb-bus */
EB_PUBLIC int eb_operation_is_config(eb_operation_t op);
/* Did this operation have an error? 1=error, 0=success */
EB_PUBLIC int eb_operation_had_error(eb_operation_t op);
/* What was the address of this operation? */
EB_PUBLIC eb_address_t eb_operation_address(eb_operation_t op);
/* What was the read or written value of this operation? */
EB_PUBLIC eb_data_t eb_operation_data(eb_operation_t op);
/* What was the format of this operation? */
EB_PUBLIC eb_format_t eb_operation_format(eb_operation_t op);

/* Read the SDWB information from the remote bus.
 * If there is not enough memory to initiate the request, EB_OOM is returned.
 * To scan the root bus, Etherbone config space is used to locate the SDWB record.
 * When scanning a child bus, supply the bridge's sdwb_device record.
 *
 * All fields in the processed structures are in machine native endian.
 * When scanning a child bus, nested addresses are automatically converted.
 *
 * Your callback is called from either eb_socket_poll or eb_device_flush.
 * It receives these arguments: (user_data, sdwb, sdwb_len, status)
 *
 * If status != OK, the SDWB information could not be retrieved.
 * If status == OK, the structure was retrieved.
 *
 * The sdwb object passed to your callback is only valid until you return.
 * If you need persistent information, you must copy the memory yourself.
 */
typedef void (*sdwb_callback_t)(eb_user_data_t, eb_device_t device, sdwb_t, eb_status_t);
EB_PUBLIC eb_status_t eb_sdwb_scan_bus(eb_device_t device, sdwb_device_t bridge, eb_user_data_t data, sdwb_callback_t cb);
EB_PUBLIC eb_status_t eb_sdwb_scan_root(eb_device_t device, eb_user_data_t data, sdwb_callback_t cb);

#ifdef __cplusplus
}

#include <vector>

/****************************************************************************/
/*                                 C++ API                                  */
/****************************************************************************/
namespace etherbone {

/* Copy the types into the namespace */
typedef eb_address_t address_t;
typedef eb_data_t data_t;
typedef eb_format_t format_t;
typedef eb_status_t status_t;
typedef eb_width_t width_t;
typedef eb_descriptor_t descriptor_t;

class Socket;
class Device;
class Cycle;
class Operation;

class Handler {
  public:
    virtual status_t read (address_t address, width_t width, data_t* data) = 0;
    virtual status_t write(address_t address, width_t width, data_t  data) = 0;
};

class Socket {
  public:
    Socket();
    
    status_t open(const char* port = 0, width_t width = EB_DATAX|EB_ADDRX);
    status_t close();
    
    /* attach/detach a virtual device */
    status_t attach(sdwb_device_t device, Handler* handler);
    status_t detach(sdwb_device_t device);
    
    void poll();
    int block(int timeout_us);
    
    /* These can be used to implement your own 'block': */
    uint32_t timeout() const;
    EB_PUBLIC std::vector<descriptor_t> descriptor() const;
    void settime(uint32_t now);
    
  protected:
    Socket(eb_socket_t sock);
    eb_socket_t socket;
  
  friend class Device;
};

class Device {
  public:
    Device();
    
    status_t open(Socket socket, const char* address, width_t width = EB_ADDRX|EB_DATAX, int attempts = 5);
    status_t close();
    status_t flush();
    
    const Socket socket() const;
    Socket socket();
    
    width_t width() const;
    
  protected:
    Device(eb_device_t device);
    eb_device_t device;
  
  friend class Cycle;
  template <typename T, void (T::*cb)(Operation, Device, status_t)>
  friend void proxy_cb(T* object, eb_device_t dev, eb_operation_t op, eb_status_t status);
};

class Cycle {
  public:
    // Start a cycle on the target device.
    template <typename T>
    Cycle(Device device, T* user, void (*cb)(T*, eb_device_t, eb_operation_t, eb_status_t));
    Cycle(Device device);
    ~Cycle(); // End of cycle = destructor
    
    void abort();
    void silent_finish();
    
    Cycle& read (address_t address, format_t format = EB_DATAX, data_t* data = 0);
    Cycle& write(address_t address, format_t format, data_t  data);
    
    Cycle& read_config (address_t address, format_t format = EB_DATAX, data_t* data = 0);
    Cycle& write_config(address_t address, format_t format, data_t  data);
    
    const Device device() const;
    Device device();
    
  protected:
    eb_cycle_t cycle;
    
    /* forbid copy and assignment */
    Cycle(const Cycle& o);
    Cycle& operator = (const Cycle& o);
};

class Operation {
  public:
    bool is_null  () const;
    
    /* Only call these if is_null is false */
    bool is_read  () const;
    bool is_config() const;
    bool had_error() const;
    
    address_t address() const;
    data_t    data   () const;
    format_t  format () const;
    
    const Operation next() const;
    Operation next();
    
  protected:
    Operation(eb_operation_t op);
    
    eb_operation_t operation;

  template <typename T, void (T::*cb)(Operation, Device, status_t)>
  friend void proxy_cb(T* object, eb_device_t dev, eb_operation_t op, eb_status_t status);
};

/* Convenience templates to convert member functions into callback type */
template <typename T, void (T::*cb)(Operation, Device, status_t)>
inline void proxy_cb(T* object, eb_device_t dev, eb_operation_t op, eb_status_t status) {
  return (object->*cb)(Operation(op), Device(dev), status);
}

/****************************************************************************/
/*                            C++ Implementation                            */
/****************************************************************************/

inline Socket::Socket(eb_socket_t sock)
 : socket(sock) { 
}

inline Socket::Socket()
 : socket(EB_NULL) {
}
inline status_t Socket::open(const char* port, width_t width) {
  return eb_socket_open(EB_ABI_CODE, port, width, &socket);
}

inline status_t Socket::close() {
  status_t out = eb_socket_close(socket);
  if (out == EB_OK) socket = EB_NULL;
  return out;
}

/* Proxy */
EB_PUBLIC eb_status_t eb_proxy_read_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t* ptr);
EB_PUBLIC eb_status_t eb_proxy_write_handler(eb_user_data_t data, eb_address_t address, eb_width_t width, eb_data_t value);

inline status_t Socket::attach(sdwb_device_t device, Handler* handler) {
  struct eb_handler h;
  h.device = device;
  h.data = handler;
  h.read  = &eb_proxy_read_handler;
  h.write = &eb_proxy_write_handler;
  return eb_socket_attach(socket, &h);
}

inline status_t Socket::detach(sdwb_device_t device) {
  return eb_socket_detach(socket, device);
}

inline void Socket::poll() {
  eb_socket_poll(socket);
}

inline int Socket::block(int timeout_us) {
  return eb_socket_block(socket, timeout_us);
}

inline uint32_t Socket::timeout() const {
  return eb_socket_timeout(socket);
}

inline void Socket::settime(uint32_t now) {
  return eb_socket_settime(socket, now);
}

inline Device::Device(eb_device_t dev)
 : device(dev) {
}

inline Device::Device()
 : device(EB_NULL) { 
}
    
inline status_t Device::open(Socket socket, const char* address, width_t width, int attempts) {
  return eb_device_open(socket.socket, address, width, attempts, &device);
}
    
inline status_t Device::close() {
  status_t out = eb_device_close(device);
  if (out == EB_OK) device = EB_NULL;
  return out;
}

inline const Socket Device::socket() const {
  return Socket(eb_device_socket(device));
}

inline Socket Device::socket() {
  return Socket(eb_device_socket(device));
}

inline width_t Device::width() const {
  return eb_device_width(device);
}

inline status_t Device::flush() {
  return eb_device_flush(device);
}

template <typename T>
inline Cycle::Cycle(Device device, T* user, void (*cb)(T*, eb_device_t, eb_operation_t, status_t))
 : cycle(eb_cycle_open(device.device, user, reinterpret_cast<eb_callback_t>(cb))) {
}

inline Cycle::Cycle(Device device)
 : cycle(eb_cycle_open(device.device, 0, 0)) {
}

inline Cycle::~Cycle() {
  if (cycle != EB_NULL)
    eb_cycle_close(cycle); 
}

inline void Cycle::abort() {
  if (cycle != EB_NULL)
    eb_cycle_abort(cycle);
  cycle = EB_NULL;
}

inline void Cycle::silent_finish() {
  if (cycle != EB_NULL)
    eb_cycle_close_silently(cycle);
  cycle = EB_NULL;
}

inline Cycle& Cycle::read(address_t address, format_t format, data_t* data) {
  eb_cycle_read(cycle, address, format, data);
  return *this;
}

inline Cycle& Cycle::write(address_t address, format_t format, data_t data) {
  eb_cycle_write(cycle, address, format, data);
  return *this;
}

inline Cycle& Cycle::read_config(address_t address, format_t format, data_t* data) {
  eb_cycle_read_config(cycle, address, format, data);
  return *this;
}

inline Cycle& Cycle::write_config(address_t address, format_t format, data_t data) {
  eb_cycle_write_config(cycle, address, format, data);
  return *this;
}

inline const Device Cycle::device() const {
  return Device(eb_cycle_device(cycle));
}

inline Device Cycle::device() {
  return Device(eb_cycle_device(cycle));
}

inline Operation::Operation(eb_operation_t op)
 : operation(op) {
}

inline bool Operation::is_null() const {
  return operation == EB_NULL;
}

inline bool Operation::is_read() const {
  return eb_operation_is_read(operation);
}

inline bool Operation::is_config() const {
  return eb_operation_is_config(operation);
}

inline bool Operation::had_error() const {
  return eb_operation_had_error(operation);
}

inline address_t Operation::address() const {
  return eb_operation_address(operation);
}

inline format_t Operation::format() const {
  return eb_operation_format(operation);
}

inline data_t Operation::data() const {
  return eb_operation_data(operation);
}

inline Operation Operation::next() {
  return Operation(eb_operation_next(operation));
}

inline const Operation Operation::next() const {
  return Operation(eb_operation_next(operation));
}

}

#endif

#endif