diff --git a/tools/nfipjtag/Makefile b/tools/nfipjtag/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..67abfdbff9a2fb19909a70480145b805fd31853e
--- /dev/null
+++ b/tools/nfipjtag/Makefile
@@ -0,0 +1,56 @@
+CPU=L867
+PROG := nfipjtag_main.mfip
+SRCS = nfipjtag_main.mfip.cpp nfipjtag_cycle.mfip.cpp fip.mfip.cpp jtag.cpp memname.c play.c scan.c statename.c svf.c tap.c xsvf.c
+#CLOBJS=$(CCSRCS:.cc=.$(CPU).o) $(CSRCS:.c=.$(CPU).o)
+#SRCS= $(CSRCS) $(CCSRCS)
+
+MFIP = ../..
+MCKTRTL = $(MFIP)/mockturtle/software
+
+#LDLIBS= -Wl,-rpath=$(MCKTRTL)/lib
+
+CPPFLAGS += -g -Wall -ggdb -I.-I$(MFIP) -I$(MFIP)/include  -I$(MFIP)/lib -Infipjtag/inc -Ifip_fgcd/inc -Infipjtag/libxsvf
+LDLIBS = -L$(MFIP)/lib -L$(MCKTRTL)/lib
+LDLIBS += -lmasterfip -Wl,-rpath=$(MCKTRTL)/lib -lmockturtle -lpthread -lm -lrt -lstdc++
+
+#CXXFLAGS=-g  -Wall -Wno-return-type -Wno-unused  $(CFLAGS)
+
+
+#include Make.auto
+
+vpath %.cpp nfipjtag/src
+vpath %.c nfipjtag/libxsvf
+vpath %.cpp fip_fgcd/src
+
+#================================
+# creation of executable test
+#================================
+
+all: $(PROG)
+
+nfipjtag_main.mfip: nfipjtag_main.mfip.o nfipjtag_cycle.mfip.o fip.mfip.o jtag.o play.o scan.o statename.o svf.o tap.o xsvf.o memname.o
+
+
+nfipjtag.$(CPU): $(CLOBJS)
+
+
+
+#================================
+# Cleanup
+#================================
+clean:
+	$(RM) *.o $(BAKS) *.$(CPU) $(PROG)
+
+clobber: clean         
+
+sources: $(SRCS)
+
+#===========================================
+# Automatically generate local dependencies
+#===========================================
+depend: $(SRCS)
+	mkdepend -o .'$$(CPU)'.o -- -I. $(STDFLAGS)  -- $(SRCS)
+
+#======================================================
+# DO NOT DELETE THIS LINE -- make depend depends on it.
+
diff --git a/tools/nfipjtag/fip_fgcd/inc/fip.mfip.h b/tools/nfipjtag/fip_fgcd/inc/fip.mfip.h
new file mode 100644
index 0000000000000000000000000000000000000000..62577ab708e1e04e46c91c94cebdaf3a66ab79a3
--- /dev/null
+++ b/tools/nfipjtag/fip_fgcd/inc/fip.mfip.h
@@ -0,0 +1,544 @@
+/*!
+ * @file   fip_fdm.h
+ * @brief  FIP Device Manager library declarations
+ * @author Michael Davis
+ */
+
+#ifndef __LIBFIPFDM_FIP_MFIP_H
+#define __LIBFIPFDM_FIP_MFIP_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <errno.h>
+#include <time.h>
+#include <sys/time.h>
+
+#include <masterfip/libmasterfip.h>
+
+// Types
+
+/*!
+ * FIP variable callback type.
+ *
+ * Specifies the type of callback functions which are linked to variables in the BA macrocycle.
+ */
+
+typedef void FipMfipVarCallback(struct mstrfip_dev *dev, struct mstrfip_data *data,
+		 	    struct mstrfip_irq *irq);
+
+
+
+/*!
+ * FIP error reporting callback type
+ *
+ * @param[in] error    Error code to be logged
+ */
+
+typedef int32_t LogErrorCallback(const char *error_string);
+
+
+
+/*!
+ * FIP read system time callback type
+ *
+ * @param[out] time    Current time read from the appropriate timing system
+ */
+
+typedef int32_t ReadTimeCallback(struct timeval *time);
+
+
+
+/*!
+ * FIP BA macrocycle program startup/shutdown callback type
+ */
+
+typedef void BAUpDownCallback(void);
+
+
+struct jtag_fip_per_var_desc {
+    uint32_t             id;
+    int 	             dir;
+    int 	             bsz;
+    FipMfipVarCallback  *cb;        //!< Pointer to callback function for this variable
+    void               **data_ptr;
+};
+
+struct jtag_fip_per_var_wind_desc {
+    int                         nvar;
+    struct jtag_fip_per_var_desc *varlist;
+};
+
+struct jtag_fip_aper_msg_wind_desc {
+    int end_ustime;   //!< us time in the macrocycle of
+    int max_prod_msg;
+    int max_cons_msg;
+};
+
+struct jtag_fip_wait_wind_desc {
+    int end_ustime;
+    int silent;
+};
+
+/*!
+ * All possible macrocycle windows used by FGC macrocycle
+ */
+enum jtag_fip_wind_type {
+    JTAG_FIP_PER_VAR_WIND,
+    JTAG_FIP_APER_MSG_WIND,
+    JTAG_FIP_WAIT_WIND,
+};
+
+/*
+ * FGC FIP Macrocycle is made of a concatenation of windows which can be:
+ * periodic variable window: in charge of scheduling periodic varaiables.
+ * aperiodic message window: schedules aperiodic messages
+ * wait window: to define some precise timing in the macrocycle.
+ */
+struct jtag_fip_mcycle_desc {
+    enum jtag_fip_wind_type                  type;           //!<
+    union {
+        struct jtag_fip_per_var_wind_desc    per_var_wind;   //!< periodic variable window
+	struct jtag_fip_aper_msg_wind_desc   aper_msg_wind;  //!< aperiodic message window
+	struct jtag_fip_wait_wind_desc       wait_wind;      //!< wait window
+    };
+};
+
+/*!
+ * Valid frame codes for WorldFIP frames.
+ *
+ * This specifies a subset of the frame types supported by WorldFIP, see:
+ * "FIP Device Manager Software Version 4 User Reference Manual", p.3-60.
+ *
+ * For communication with nanoFIP devices, we do not support aperiodic messages
+ * (SEND_MSG), aperiodic variables (SEND_APER), or external synchronisation waiting
+ * with stuffing (SYN_WAIT). It is OK to synch wait without stuffing because we
+ * know that our device will be Bus Arbitrator--nanoFIP devices cannot be BA.
+ */
+
+enum jtag_fip_per_var_dir
+{
+	JTAG_FIP_PER_VAR_CONS,
+	JTAG_FIP_PER_VAR_PROD,
+};
+
+
+
+/*!
+ * Struct for FIP device error statistics
+ */
+
+struct FIP_device_stats
+{
+    uint32_t var_recv_count;                 //!< Count of received variables. Reset and updated by the library.
+    uint32_t var_miss_count;                 //!< Count of missed variables. Reset by the library but updated by the application.
+    uint32_t var_late_count;                 //!< Count of received variables that missed their deadline. Reset by the library but updated by the application.
+
+    uint32_t nonsignificance_fault_count;    //!< Non-significance faults
+    uint32_t promptness_fail_count;          //!< Promptness failures
+    uint32_t significance_fault_count;       //!< Significance status
+    uint32_t user_error_count;               //!< FIP user errors
+};
+
+
+
+/*!
+ * Struct for FIP fieldbus error statistics
+ */
+
+struct FIP_fieldbus_stats
+{
+    int32_t  error_flag;                           //!< Flag to indicate that an error was detected during the gateway's WorldFIP processing
+    uint32_t start_count;                          //!< Count of WorldFIP interface starts/restarts
+    uint32_t start_time;                           //!< Time of last WorldFIP interface start
+
+    uint32_t interface_error_count;                //!< Count of WorldFIP interface errors
+    uint32_t last_interface_error;                 //!< Value of last WorldFIP interface error received
+
+    struct FIP_device_stats errors;                //!< Total FIP errors for all devices
+};
+
+
+
+/*!
+ * Struct for FIP FDM configuration
+ */
+
+struct FIP_config
+{
+    struct mstrfip_sw_cfg      sw_config;          //!< FDM Software configuration
+    struct mstrfip_hw_cfg      hw_config;          //!< FDM Hardware configuration
+    uint8_t                    thread_priority;    //!< FDM thread priority
+
+    // Callback functions
+
+    LogErrorCallback          *log_error_func;     //!< Callback to log error messages
+    ReadTimeCallback          *read_time_func;     //!< Callback to read current time
+    BAUpDownCallback          *ba_up_func;         //!< Callback when the BA macrocycle program starts up
+    BAUpDownCallback          *ba_down_func;       //!< Callback when the BA macrocycle program shuts down
+
+    // Statistics
+
+    struct FIP_fieldbus_stats *fieldbus_stats;     //!< Pointer to data structure for fieldbus statistics
+};
+
+
+
+// Constants
+
+static const uint8_t  FIP_DIAG_DEV_ADDR               = 0x7F;       //!< Address of the WorldFIP diagnostic device on the bus
+
+static const uint8_t  FIP_DEFAULT_THREAD_PRIORITY     = 65;         //!< Pritority of the FIP FDM thread
+static const uint8_t  FIP_WATCHDOG_THREAD_PRIORITY    = 37;         //!< Priority of the Watchdog thread.
+
+static const uint32_t FIP_THREAD_STACK_SIZE           = 1048576;    //!< Pthread stack size.
+
+
+
+// Inline function declarations
+
+/*!
+ * Sleep for the specified no. of milliseconds.
+ *
+ * Note that nanosleep is thread-safe.
+ *
+ * @param[in] ms    No. of milliseconds to sleep
+ */
+
+static inline void msSleep(uint16_t ms)
+{
+    struct timespec time;
+
+    time.tv_sec  = ms / 1000;
+    time.tv_nsec = (ms % 1000) * 1000000;
+
+    // sleep for the specified no. of ms and resume if interrupted
+
+    while(nanosleep(&time, &time) && errno == EINTR) ;
+}
+
+
+/*!
+ * Get device ID from MPS variable context.
+ *
+ * See "FIP DEVICE MANAGER Software Version 4 User Reference Manual", p.3-34.
+ *
+ * @param[in] fdm_mps_var_ref    MPS variable context
+ *
+ * @returns FIP address of the device sending the status
+ */
+
+static inline uint8_t fipMfipGetDeviceId(struct mstrfip_data *data)
+{
+    return data->id & 0xFF;
+}
+
+// External functions
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Reset device stats to zero.
+ *
+ * @param[out] stats    Device stats struct to update.
+ */
+
+void fipMfipResetDeviceStats(struct FIP_device_stats *stats);
+
+
+
+/*!
+ * Returns the current time.
+ *
+ * Calls the time callback specified in read_time_func() above, if defined.
+ * Otherwise use gettimeofday().
+ *
+ * @param[out] timeNow    struct to overwrite with current time in seconds and microseconds
+ *
+ * @returns The current time in seconds.
+ */
+
+int32_t fipMfipGetTime(struct timeval *timeNow);
+
+
+
+/*!
+ * Log an error message.
+ *
+ * Calls the logging callback specified in log_error_func() above, if defined.
+ * Otherwise write the log message to stderr.
+ *
+ * @param[in] format    printf-style format string and arguments
+ *
+ * @returns The result of the call to fprintf or the specified callback. By convention, the result should be
+ *          the number of characters written in the case of success, or a negative number in case of failure.
+ */
+
+int32_t fipMfipLogError(const char *format, ...);
+
+
+
+/*!
+ * Get the current FDM configuration.
+ *
+ * If FDM has not yet been configured, fipGetConfig() returns a set of default values.
+ *
+ * @param[out] config    Pointer to struct to hold returned config data
+ *
+ * @retval  0   Configuration returned successfully
+ * @retval -1   Failure to initialise default configuration. The error code is set in errno.
+ */
+
+int32_t fipMfipGetConfig(struct FIP_config *config);
+
+
+
+/*!
+ * Set the FDM configuration.
+ *
+ * Note that this function does not check that the configuration is valid.
+ *
+ * @param[in] config    Pointer to struct containing config values to set
+ *
+ * @retval  0   Configuration set successfully
+ * @retval -1   Set configuration failed. The error code is set in errno.
+ */
+
+int32_t fipMfipSetConfig(struct FIP_config *config);
+
+
+
+/*!
+ * Start the FIP interface and FIP Device Manager.
+ *
+ * @retval  0   FIP interface started successfully
+ * @retval -1   Starting the FIP interface failed. The error is code is set in errno.
+ */
+
+int32_t fipMfipStartInterface(void);
+
+
+
+/*!
+ * Stop the FIP interface.
+ */
+
+void fipMfipStopInterface(void);
+
+
+
+/*!
+ * Restart the FIP interface.
+ */
+
+int32_t fipMfipRestartInterface(void);
+
+
+
+/*!
+ * Initialise the FDM Bus Arbitrator macrocycle program.
+ *
+ * @param[in] program    Array of WorldFIP frames to be loaded into the program
+ * @param[in] nframes    No. of frames (size of the program array)
+ */
+
+int32_t fipMfipInitProtocol(const struct jtag_fip_mcycle_desc *mcycle_desc,
+		     uint32_t nentries);
+
+
+
+/*!
+ * Start the FDM Bus Arbitrator macrocycle program.
+ *
+ * @retval  0   FDM BA started successfully
+ * @retval -1   Starting the FDM BA failed. The error code is set in errno.
+ */
+
+int32_t fipMfipStartProtocol(void);
+
+
+
+/*!
+ * Shut down the FDM Bus Arbitrator macrocycle program.
+ */
+
+void fipMfipStopProtocol(void);
+
+
+
+/*!
+ * Restart the FDM Bus Arbitrator macrocycle program.
+ */
+
+int32_t fipMfipRestartProtocol(void);
+
+
+
+/*!
+ * Write a single Producer variable value into its FDM context
+ *
+ * @param[in] id_dat    FIP frame ID of the variable to be written
+ */
+
+void fipMfipWriteVar(uint32_t id_dat);
+
+
+
+/*!
+ * Write all the Producer variable values into their FDM contexts
+ */
+
+void fipMfipWriteVars(void);
+
+
+
+/*!
+ * Read a Consumer variable value from its FDM context
+ *
+ * @param[in]     var_id         ID of the variable to read
+ * @param[in,out] error_stats    WorldFIP error stats to update
+ *
+ * @retval true     Variable was read successfully
+ * @retval false    An error occurred while reading the variable
+ */
+
+bool fipMfipReadVar(struct mstrfip_data *data, struct FIP_device_stats *error_stats);
+
+
+
+/*!
+ * Get time of last WorldFIP interface start.
+ *
+ * @returns UNIX time that the WorldFIP interface was started
+ */
+
+uint32_t fipMfipGetStartTime(void);
+
+
+
+/*!
+ * Report whether the WorldFIP interface is running
+ *
+ * @retval true if interface is running
+ * @retval false if interface is not running
+ */
+
+bool fipMfipIsInterfaceUp(void);
+
+
+
+/*!
+ * Increment cycle count.
+ *
+ * Call this function once per FIP FDM cycle, probably from within a FIP producer callback.
+ *
+ * @returns Updated cycle count (after incrementing)
+ */
+
+uint32_t fipMfipCycleTick(void);
+
+
+
+/*!
+ * Get the number of FIP FDM cycles.
+ */
+
+uint32_t fipMfipGetCycleCount(void);
+
+
+
+/*!
+ * Register a device with FDM and Diamon.
+ *
+ * This lets FDM and Diamon know if a device should be present. The Diamon error bit for
+ * each device is the XOR of the registered and present bits.
+ *
+ * @param[in] address    Address of the device on the WorldFIP bus
+ */
+
+void fipMfipConfigureDevice(uint8_t address);
+
+/*!
+ * Unregister a device with FDM and Diamon.
+ *
+ * This lets FDM and Diamon know if a device should be present. The Diamon error bit for
+ * each device is the XOR of the registered and present bits.
+ *
+ * @param[in] address    Address of the device on the WorldFIP bus
+ */
+
+ void fipMfipUnconfigureDevice(uint8_t address);
+ 
+
+/*!
+ * Determine if the device has been configured and registered with FDM and Diamon.
+ *
+ * @param[in] address    Address of the device on the WorldFIP bus
+ *
+ * @retval true    if the device was previously registered with fipConfigureDevice()
+ * @retval false   if the device has not been registered
+ */
+
+bool fipMfipIsDeviceConfigured(uint8_t address);
+
+
+/*!
+ * Copy the status of which devices are configured into shared memory
+ *
+ * This is used to copy device statuses to Diamon.
+ *
+ * @param[in] ptr    Pointer to shared memory area (target for copy operation)
+ */
+
+void fipMfipGetAllConfigured(uint8_t *ptr);
+
+
+
+/*!
+ * Set the status of a device as present.
+ *
+ * @param[in] address    Address of the device in the range 0..255. Address 0 is reserved for the front-end.
+ */
+
+void fipMfipSetDevicePresent(uint8_t address);
+
+
+
+/*!
+ * Set the status of a device as not present.
+ *
+ * @param[in] address    Address of the device in the range 0..255. Address 0 is reserved for the front-end.
+ */
+
+void fipMfipSetDeviceNotPresent(uint8_t address);
+
+
+
+/*!
+ * Report whether a device is present.
+ *
+ * @param[in] address    Address of the device in the range 0..255. Address 0 is reserved for the front-end.
+ */
+
+bool fipMfipIsDevicePresent(uint8_t address);
+
+
+
+/*!
+ * Copy the status of which devices are present into shared memory.
+ *
+ * This is used to copy device statuses to Diamon.
+ *
+ * @param[in] ptr    Pointer to shared memory area (target for copy operation)
+ */
+
+void fipMfipGetAllPresent(uint8_t *ptr);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/fip_fgcd/inc/fip_errno.h b/tools/nfipjtag/fip_fgcd/inc/fip_errno.h
new file mode 100644
index 0000000000000000000000000000000000000000..63f991d999e6480e96962918602a84ea7851aa86
--- /dev/null
+++ b/tools/nfipjtag/fip_fgcd/inc/fip_errno.h
@@ -0,0 +1,33 @@
+/*!
+ * @file   fip_errno.h
+ * @brief  Error codes for FIP FDM library functions
+ * @author Michael Davis
+ */
+
+#ifndef __LIBFIPFDM_ERRNO_H
+#define __LIBFIPFDM_ERRNO_H
+
+/*!
+ * Error codes for FIP FDM initialisation and startup functions
+ */
+
+enum FIP_errno
+{
+    INIT_SUCCESS,                        //!< The function completed successfully
+
+    INIT_NOT_INITIALISED        = 1000,  //!< Initialisation relies on a prior initialisation step which has not been performed
+    INIT_ALREADY_INITIALISED,            //!< Tried to initialise a data structure that has already been initialised
+    INIT_NOT_RUNNING,                    //!< Tried to start a function that relies on a service which is not running
+    INIT_ALREADY_RUNNING,                //!< Tried to start a function that is already running
+    INIT_SEM_FAILED,                     //!< Failed to initialise a semaphore
+    INIT_MUTEX_FAILED,                   //!< Failed to initialise a mutex
+    INIT_PTHREAD_FAILED,                 //!< Failed to initialise pthreads
+    INIT_QUEUE_FAILED,                   //!< Failed to initialise a queue data structure
+    INIT_FDM_BA_FAILED,                  //!< Failed to initialise FIP FDM Bus Arbitrator (BA)
+    INIT_FDM_AE_LE_FAILED,               //!< Failed to initialise FIP FDM Application Entity/Link Entity (AE/LE) layer
+    INIT_FDM_MPS_FAILED                  //!< Failed to initialise FIP FDM MPS Variables
+};
+
+#endif
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/fip_fgcd/src/fip.mfip.cpp b/tools/nfipjtag/fip_fgcd/src/fip.mfip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..837b5915e659da789ad853f550207a474d83fcb1
--- /dev/null
+++ b/tools/nfipjtag/fip_fgcd/src/fip.mfip.cpp
@@ -0,0 +1,1004 @@
+/*!
+ * @file   fip_mfip.cpp
+ * @brief  FIP Device Manager layer
+ * @author Michael Davis
+ *
+ * The functions in this file assume that there is a single WorldFIP segment, and all devices connected to it
+ * (except this one) have nanoFIP controllers. This means that:
+ *
+ * - this device will be bus master
+ * - periodic variables are implemented
+ * - aperiodic variables, periodic messages and aperiodic messages are not implemented
+ *
+ * To develop this into library code, it should be extended to include messages and aperiodic variables (not
+ * required for FGClite).
+ */
+
+#include <cstdarg>
+#include <sys/time.h>
+#include <pthread.h>
+#include <assert.h>
+#include <string.h>
+#include <stdio.h>
+#include <unistd.h>
+
+// FIP includes
+
+#include <fip_errno.h>
+#include <fip.mfip.h>
+
+// Types
+
+/*!
+ * FIP macrocycle definition.
+ * See "FIP DEVICE MANAGER Software Version 4 User Reference Manual", p.3-59 to 3-63 for details.
+ */
+
+struct FIP_BA_Cycle
+{
+    bool                      is_initialised;       // Flag to indicate whether the FIP macrocycle configuration has been initialised
+    bool                      is_running;
+    struct mstrfip_macrocycle *mcycle;              // macro cycle object
+    struct mstrfip_data       **per_varlist;        // Sorted list of all periodic variables
+    uint32_t                  n_per_var;            // No. of variables to be created
+    struct mstrfip_data	      **cons_per_varlist;   // Sorted list of consumed variables
+    uint32_t                  n_cons_per_var;       // No. of variables to be created
+    struct mstrfip_data	      **prod_per_varlist;   // Sorted list of produced variables
+    uint32_t                  n_prod_per_var;       // No. of variables to be created
+};
+
+/*!
+ * @brief FIP Device Manager structure
+ */
+
+struct FIP_MFIP
+{
+    bool                       is_initialised;        // Flag to indicate whether the FDM configuration has been initialised
+    bool                       is_running;            // Flag to indicate that the interface is running
+
+    pthread_mutex_t            fip_mutex;             // Mutex to protect starting and stopping FIP interface
+    pthread_mutex_t            ba_mutex;              // Mutex to protect starting and stopping BA program
+
+    struct mstrfip_dev         *dev;                  // MasterFip device
+    struct FIP_config          config;                // FIP configuration
+
+    struct FIP_BA_Cycle        ba;                    // FIP macrocycle definition
+    uint32_t                   cycle_count;           // FIP cycle count
+
+    // Device statuses
+
+    uint8_t                    device_configured[32]; // One bit per WorldFIP address. 0 is not configured, 1 is configured. 32x8 bits = 256 addresses, to match the addressing model of Diamon.
+    uint8_t                    device_present[32];    // Presence bits, one bit per WorldFIP address, as above.
+
+    // Statistics
+
+    struct FIP_fieldbus_stats  stats_default;         // Default data storage for fieldbus statistics
+};
+
+// Constants
+
+const uint32_t                LOG_MESSAGE_MAX_LEN          = 512;                       // Maximum length of a log message
+
+// Variables for FDM_Configuration_Soft - these can be changed in required using fipFdmSetConfigSoft()
+
+static enum mstrfip_bitrate   FIP_BUS_SPEED                = MSTRFIP_BITRATE_2500;       //!< FGC app expects 2.5Mb/s FIP bus
+
+static uint32_t               FIP_TURNAROUND_USTIME        = 13;                     // Minimum turnaround time -
+
+
+// Constants for FDM_Configuration_Soft
+
+// Variables for FDM_Configuration_Hard - these can be changed in required using fipFdmSetConfigHard()
+
+static uint16_t               FIP_MEZZANINE_NR             = 0;                         // FIP logical unit card number, e.g. 1 for fip_u01
+
+
+
+// Global variables
+
+struct FIP_MFIP fip_mfip = {};    // initialise all values to zero
+
+
+
+// FDM error and warning callback functions
+
+static void fipMfipInterfaceFatalError(struct mstrfip_dev *dev,
+				   enum mstrfip_error_list error);
+
+
+// Inline functions
+
+/*
+ * FDM Software configuration
+ */
+
+static void fipMfipInitSoft(void)
+{
+    fip_mfip.config.sw_config.mstrfip_error_handler   = fipMfipInterfaceFatalError;
+    fip_mfip.config.sw_config.irq_thread_prio = FIP_DEFAULT_THREAD_PRIORITY;
+}
+
+
+
+/*
+ * FDM Hardware configuration
+ */
+
+static void fipMfipInitHard(void)
+{
+    fip_mfip.config.hw_config.enable_ext_trig = 1;      /* Enable external trigger */
+    fip_mfip.config.hw_config.enable_int_trig = 0;      /* Disable internal trigger */
+    fip_mfip.config.hw_config.enable_ext_trig_term = 1; /* No 50ohms term on external trigger */
+    fip_mfip.config.hw_config.turn_around_ustime = FIP_TURNAROUND_USTIME;
+}
+
+
+
+// Static functions
+
+
+
+/*
+ * Callback on interface fatal error.
+ *
+ * When the FDM detects a FATAL_ERROR, this callback function is called. Error codes are defined in enum Code_Error
+ * in fdm.h. See "FIP DEVICE MANAGER Software Version 4 User Reference Manual", Chapter 5 for a detailed description
+ * of error codes.
+ */
+
+static void fipMfipInterfaceFatalError(struct mstrfip_dev *dev,
+				   enum mstrfip_error_list error)
+{
+     
+    if(fip_mfip.config.fieldbus_stats->last_interface_error != static_cast<uint32_t>(error))
+    {
+        fipMfipLogError("fipInterfaceFatalError: %s (error = %u)\n", mstrfip_strerror(error), error);
+    }
+
+    fip_mfip.config.fieldbus_stats->error_flag           = 1;
+    fip_mfip.config.fieldbus_stats->last_interface_error = error;
+    fip_mfip.config.fieldbus_stats->interface_error_count++;
+}
+
+
+
+/*
+ * Initialise MPS command and status variables.
+ * See "FIP DEVICE MANAGER Software Version 4 User Reference Manual", p.3-32 to 3-35
+ */
+
+static int32_t fipMfipCreatePerVars(const struct jtag_fip_mcycle_desc *mcycle_desc,
+			        uint32_t nentries)
+{
+    struct mstrfip_data *per_var;
+    struct mstrfip_data_cfg var_cfg;
+    int idx_var = 0, idx_cons_var = 0, idx_prod_var = 0;
+
+    for (uint32_t i = 0; i < nentries; ++i) {
+    	if (mcycle_desc[i].type == JTAG_FIP_PER_VAR_WIND) {
+	    const struct jtag_fip_per_var_wind_desc *pwind = &(mcycle_desc[i].per_var_wind);
+	    for (int j = 0; j < pwind->nvar; ++j) {
+	    	var_cfg.id = pwind->varlist[j].id;
+		var_cfg.flags = (pwind->varlist[j].dir == JTAG_FIP_PER_VAR_CONS) ?
+			      MSTRFIP_DATA_FLAGS_CONS : MSTRFIP_DATA_FLAGS_PROD;
+	        var_cfg.max_bsz = pwind->varlist[j].bsz;
+		var_cfg.mstrfip_data_handler = pwind->varlist[j].cb;
+		per_var = mstrfip_var_create(fip_mfip.ba.mcycle, &var_cfg);
+		if (per_var == NULL) {
+		    fipMfipLogError("%s() creating periodic var id 0x%x failed: %s\n",
+				__func__, var_cfg.id, strerror(errno));
+		    return -1;
+		}
+		// Set the data_ptr pointing to the fip var buffer
+		*pwind->varlist[j].data_ptr = per_var->buffer;
+
+		// register the pointer to the new created var
+		fip_mfip.ba.per_varlist[idx_var] = per_var;
+		++idx_var;
+		if (var_cfg.flags == MSTRFIP_DATA_FLAGS_CONS) {
+		    fip_mfip.ba.cons_per_varlist[idx_cons_var] = per_var;
+		    ++idx_cons_var;
+		}
+		else {
+		    fip_mfip.ba.prod_per_varlist[idx_prod_var] = per_var;
+		    ++idx_prod_var;
+		}
+	    }
+	}
+    }
+
+    return 0;
+}
+
+
+static int32_t fipMfipBuildMacrocycle(const struct jtag_fip_mcycle_desc *mcycle_desc,
+                                  uint32_t nentries)
+{
+    struct mstrfip_per_var_wind_cfg pwind_cfg;
+    int res;
+
+    for (uint32_t i = 0, idx_var = 0; i < nentries; ++i) {
+    	switch (mcycle_desc[i].type) {
+	case JTAG_FIP_PER_VAR_WIND:
+    	    /*
+	     * per_varlist contains all periodic variables to be played during
+	     * a macrocycle, sorted in the right order.
+	     * According to the macro cycle description, we build the required
+	     * periodic window giving the right entry in the per_varlist and of
+	     * course the number of periodic var for this window.
+	     */
+	    pwind_cfg.varlist = &fip_mfip.ba.per_varlist[idx_var];
+    	    pwind_cfg.var_count = mcycle_desc[i].per_var_wind.nvar;
+	    idx_var += mcycle_desc[i].per_var_wind.nvar; // increment idx for next window
+            res = mstrfip_per_var_wind_append(fip_mfip.ba.mcycle, &pwind_cfg);
+	    if (res < 0) {
+   	        fipMfipLogError("%s() appending periodic window failed: %s\n",
+			    __func__, strerror(errno));
+		return -1;
+	    }
+	    break;
+
+	case JTAG_FIP_APER_MSG_WIND:
+	    // Not implemented
+	    break;
+
+	case JTAG_FIP_WAIT_WIND :
+	    res = mstrfip_wait_wind_append(fip_mfip.ba.mcycle,
+	    				   mcycle_desc[i].wait_wind.silent,
+					   mcycle_desc[i].wait_wind.end_ustime);
+	    if (res < 0) {
+   	        fipMfipLogError("%s() appending wait window failed: %s\n",
+			    __func__, strerror(errno));
+		return -1;
+	    }
+	    break;
+	}
+    }
+    return 0;
+}
+
+
+/*
+ * One-time FIP FDM initialisation
+ */
+
+static int32_t fipMfipDefaultInit(void)
+{
+    pthread_mutexattr_t mutex_attr;
+
+    // Statically-initialised mutex for one-time initialisation
+
+    static pthread_mutex_t init_lock = PTHREAD_MUTEX_INITIALIZER;
+
+    pthread_mutex_lock(&init_lock);
+
+    if(fip_mfip.is_initialised)
+    {
+        errno = INIT_ALREADY_INITIALISED;
+        pthread_mutex_unlock(&init_lock);
+        return 0;
+    }
+
+    // Initialise FIP FDM mutex
+
+    if(pthread_mutexattr_init(&mutex_attr) != 0)
+    {
+        errno = INIT_MUTEX_FAILED;
+        pthread_mutex_unlock(&init_lock);
+        return -1;
+    }
+
+    if(pthread_mutexattr_setprotocol(&mutex_attr, PTHREAD_PRIO_INHERIT) != 0  ||
+       pthread_mutex_init(&fip_mfip.fip_mutex, &mutex_attr)              != 0  ||
+       pthread_mutex_init(&fip_mfip.ba_mutex,  &mutex_attr)              != 0)
+    {
+        pthread_mutexattr_destroy(&mutex_attr);
+        errno = INIT_MUTEX_FAILED;
+        pthread_mutex_unlock(&init_lock);
+        return -1;
+    }
+    pthread_mutexattr_destroy(&mutex_attr);
+
+    // Initialise FIP FDM configuration
+
+    fipMfipInitSoft();
+    fipMfipInitHard();
+
+    fip_mfip.config.thread_priority = FIP_DEFAULT_THREAD_PRIORITY;
+    fip_mfip.config.fieldbus_stats  = &fip_mfip.stats_default;
+    fip_mfip.is_initialised         = true;
+
+    // Register the front-end and diagnostic device as configured
+
+    fipMfipConfigureDevice(0);
+    fipMfipConfigureDevice(FIP_DIAG_DEV_ADDR);
+
+    errno = INIT_SUCCESS;
+
+    pthread_mutex_unlock(&init_lock);
+
+    return 0;
+}
+
+static int32_t fipMfipOpenDevice()
+{
+    int res;
+
+    res = mstrfip_init();
+    if (res)
+    {
+        fprintf(stderr, "ERROR: Cannot init fip library: %s\n",
+		mstrfip_strerror(errno));
+	return res;
+    }
+
+    fip_mfip.dev = mstrfip_open_by_lun(FIP_MEZZANINE_NR);
+    if(fip_mfip.dev == NULL)
+    {
+        fprintf(stderr, "ERROR: Cannot open masterfip dev: %s\n",
+	        mstrfip_strerror(errno));
+        return res;
+    }
+
+    /* reset mockturtle RT app */
+    if (mstrfip_rtapp_reset(fip_mfip.dev) < 0)
+    {
+        fprintf(stderr, "ERROR: Cannot reset rt app: %s\n",
+		mstrfip_strerror(errno));
+	return res;
+    }
+    return 0;
+}
+
+
+static int32_t fipMfipReadBusSpeed()
+{
+    int res;
+    enum mstrfip_bitrate speed;
+    char *speed_str;
+
+    res = mstrfip_hw_speed_get(fip_mfip.dev, &speed);
+    if (res)
+    {
+	fprintf(stderr, "ERROR: Can't get FIP speed: %s. Exit\n",
+		mstrfip_strerror(errno));
+	return res;
+    }
+
+    if (speed != FIP_BUS_SPEED)
+    {
+        switch (speed)
+        {
+	case MSTRFIP_BITRATE_31:
+	   speed_str = (char *)"31.25 kb/s";
+	   break;
+	case MSTRFIP_BITRATE_1000:
+	   speed_str = (char *)"1 Mb/s";
+	   break;
+	case MSTRFIP_BITRATE_2500:
+	   speed_str = (char *)"2.5 Mb/s";
+	   break;
+	case MSTRFIP_BITRATE_UNDEFINED:
+	   speed_str = (char *)"Undefined speed";
+	   break;
+        default: // This case should never occur
+	   speed_str = (char *)"Unexpected speed returned by mstrfip_hw_speed_get()";
+	   break;
+	}
+
+        fprintf(stderr, "ERROR: Expected FIP bus speed 2.5 Mb/s and got %s\n",
+				speed_str);
+	return 1;
+    }
+    return 0;
+}
+
+// External functions
+
+void fipMfipSetConfigHard(const uint16_t fip_mezzanine_nr,
+		      const uint32_t fip_turnaround_ustime)
+{
+    FIP_MEZZANINE_NR = fip_mezzanine_nr;
+
+    if(fip_turnaround_ustime > 0)
+    {
+        FIP_TURNAROUND_USTIME = fip_turnaround_ustime;
+    }
+}
+
+
+
+void fipMfipResetFieldbusStats(struct FIP_fieldbus_stats *stats)
+{
+    stats->interface_error_count = 0;
+}
+
+
+
+void fipMfipResetDeviceStats(struct FIP_device_stats *stats)
+{
+    stats->var_recv_count              = 0;
+    stats->var_miss_count              = 0;
+    stats->var_late_count              = 0;
+    stats->nonsignificance_fault_count = 0;
+    stats->promptness_fail_count       = 0;
+    stats->significance_fault_count    = 0;
+    stats->user_error_count            = 0;
+}
+
+
+
+int32_t fipMfipGetTime(struct timeval *timeNow)
+{
+    if(fip_mfip.config.read_time_func == NULL)
+    {
+        gettimeofday(timeNow, NULL);
+    }
+    else
+    {
+        fip_mfip.config.read_time_func(timeNow);
+    }
+
+    return timeNow->tv_sec;
+}
+
+
+
+int32_t fipMfipLogError(const char *format, ...)
+{
+    va_list     args;
+    char buffer[LOG_MESSAGE_MAX_LEN];
+    int32_t     length;
+
+    // Write to stderr by default
+
+    if(!fip_mfip.is_initialised || fip_mfip.config.log_error_func == NULL)
+    {
+        va_start(args, format);
+        length = vfprintf(stderr, format, args);
+        va_end(args);
+
+        return length;
+    }
+
+    // Write variable arguments into the buffer
+
+    va_start(args, format);
+    length = vsnprintf(buffer, LOG_MESSAGE_MAX_LEN - 4, format, args);
+    va_end(args);
+
+    if(length == LOG_MESSAGE_MAX_LEN - 4)
+    {
+        // Replace final characters of message with ellipsis to indicate that the message is incomplete
+
+        strcpy(buffer + LOG_MESSAGE_MAX_LEN - 5, "...\n");
+    }
+
+    if(length > 0)
+    {
+        length = fip_mfip.config.log_error_func(buffer);
+    }
+
+    return length;
+}
+
+
+
+int32_t fipMfipGetConfig(struct FIP_config *config)
+{
+    if(fipMfipDefaultInit() != 0) return -1;
+
+    pthread_mutex_lock(&fip_mfip.fip_mutex);
+    memcpy(config, &fip_mfip.config, sizeof(struct FIP_config));
+    pthread_mutex_unlock(&fip_mfip.fip_mutex);
+
+    return 0;
+}
+
+
+
+int32_t fipMfipSetConfig(struct FIP_config *config)
+{
+    if(fipMfipDefaultInit() != 0) return -1;
+
+    pthread_mutex_lock(&fip_mfip.fip_mutex);
+
+    // It is not possible to reconfigure a running interface
+
+    if(fip_mfip.is_running)
+    {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+        errno = INIT_ALREADY_RUNNING;
+        return -1;
+    }
+
+    memcpy(&fip_mfip.config, config, sizeof(struct FIP_config));
+
+    pthread_mutex_unlock(&fip_mfip.fip_mutex);
+
+    return 0;
+}
+
+
+
+int32_t fipMfipStartInterface(void)
+{
+    struct timeval timeNow;
+
+    // One-time initialisation
+
+    if(!fip_mfip.is_initialised)
+    {
+        if(fipMfipDefaultInit() != 0) return -1;
+    }
+
+    // Protect starting and stopping
+
+    pthread_mutex_lock(&fip_mfip.fip_mutex);
+
+    // Check whether interface should already be running
+
+    if(fip_mfip.is_running)
+    {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+        errno = INIT_ALREADY_RUNNING;
+        return -1;
+    }
+
+    // Increment start count, set start time and reset error flag
+
+    fip_mfip.config.fieldbus_stats->start_count++;
+    fip_mfip.config.fieldbus_stats->start_time = fipMfipGetTime(&timeNow);
+
+    fip_mfip.config.fieldbus_stats->error_flag = 0;
+    fip_mfip.config.fieldbus_stats->last_interface_error = 0;
+
+    // Open masterFIP device. Returns 0 on success. Returns -1 and sets errno on failure.
+    if (fipMfipOpenDevice()) {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+    	return -1;
+    }
+
+    // Check the FIP bus speed: 2.5MB/s is expected
+    if (fipMfipReadBusSpeed()) {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+    	return -1;
+    }
+
+    // Send Sw config to the masterFIP device
+    if (mstrfip_sw_cfg_set(fip_mfip.dev, &fip_mfip.config.sw_config) < 0) {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+    	return -1;
+    }
+
+    // Send HW config to the masterFIP device
+    if (mstrfip_hw_cfg_set(fip_mfip.dev, &fip_mfip.config.hw_config) < 0) {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+    	return -1;
+    }
+
+
+    // Set run flag
+
+    fip_mfip.is_running = true;
+
+    errno = INIT_SUCCESS;
+
+    pthread_mutex_unlock(&fip_mfip.fip_mutex);
+
+    return 0;
+}
+
+
+void fipMfipStopInterface(void)
+{
+    if(!fip_mfip.is_initialised) return;
+
+    // Protect starting and stopping
+
+    pthread_mutex_lock(&fip_mfip.fip_mutex);
+
+    // Check whether the interface is already stopped
+
+    if(!fip_mfip.is_running)
+    {
+        pthread_mutex_unlock(&fip_mfip.fip_mutex);
+        return;
+    }
+
+    fip_mfip.is_running = false;
+
+    // Stop BA macrocycle program
+
+    fipMfipStopProtocol();
+
+    pthread_mutex_unlock(&fip_mfip.fip_mutex);
+}
+
+
+
+int32_t fipMfipRestartInterface(void)
+{
+    int32_t result;
+    int32_t cancel_state_orig;
+
+    static pthread_mutex_t restart_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state_orig);
+    pthread_mutex_lock(&restart_mutex);
+
+    fipMfipStopInterface();
+
+    result = fipMfipStartInterface();
+    if(result == 0)
+    {
+        result = fipMfipStartProtocol();
+    }
+
+    pthread_mutex_unlock(&restart_mutex);
+    pthread_setcancelstate(cancel_state_orig, &cancel_state_orig);
+
+    return result;
+}
+
+
+
+int32_t fipMfipInitProtocol(const struct jtag_fip_mcycle_desc *mcycle_desc,
+			uint32_t nentries)
+{
+    int res;
+
+    // clean up if this is a redefinition
+
+    if(fip_mfip.ba.is_initialised)
+    {
+        delete[] fip_mfip.ba.per_varlist;        // Sorted list of all periodic variables
+        delete[] fip_mfip.ba.cons_per_varlist;   // Sorted list of consumed variables
+        delete[] fip_mfip.ba.prod_per_varlist;   // Sorted list of produced variables
+    	fip_mfip.ba.n_per_var = 0;
+    	fip_mfip.ba.n_cons_per_var = 0;
+    	fip_mfip.ba.n_prod_per_var = 0;
+    }
+
+    // Create macro cycle object
+    fip_mfip.ba.mcycle = mstrfip_macrocycle_create(fip_mfip.dev);
+    if (fip_mfip.ba.mcycle == NULL) 
+    {
+        return -1;
+    }
+
+    // Count no. of periodic var consumed and produced
+    for (uint32_t i = 0; i < nentries; ++i) {
+    	if (mcycle_desc[i].type == JTAG_FIP_PER_VAR_WIND) {
+	    for (int j = 0; j < mcycle_desc[i].per_var_wind.nvar; ++j) {
+	        (mcycle_desc[i].per_var_wind.varlist[j].dir == JTAG_FIP_PER_VAR_CONS) ?
+		++fip_mfip.ba.n_cons_per_var : ++fip_mfip.ba.n_prod_per_var;
+	    }
+	}
+    }
+    // total number of periodic variables is the sum
+    fip_mfip.ba.n_per_var = fip_mfip.ba.n_cons_per_var + fip_mfip.ba.n_prod_per_var;
+
+    // Allocate arrays for macrocycle variables
+    fip_mfip.ba.per_varlist = new mstrfip_data*[fip_mfip.ba.n_per_var];
+    fip_mfip.ba.cons_per_varlist = new mstrfip_data*[fip_mfip.ba.n_cons_per_var];
+    fip_mfip.ba.prod_per_varlist = new mstrfip_data*[fip_mfip.ba.n_prod_per_var];
+
+    // initialise periodic variables
+    res = fipMfipCreatePerVars(mcycle_desc, nentries);
+    if (res == -1)
+    	return res;
+
+    // Build instruction lists in FIP FDM format
+    res = fipMfipBuildMacrocycle(mcycle_desc, nentries);
+    if (res == -1)
+    	return res;
+
+    fip_mfip.ba.is_initialised = true;
+    return 0;
+}
+
+
+
+int32_t fipMfipStartProtocol(void)
+{
+    // Check that initialisation has been performed
+
+    if(!fip_mfip.is_initialised || !fip_mfip.ba.is_initialised)
+    {
+        errno = INIT_NOT_INITIALISED;
+        return -1;
+    }
+
+    // Protect starting and stopping
+
+    pthread_mutex_lock(&fip_mfip.ba_mutex);
+
+    if(!fip_mfip.is_running)
+    {
+        pthread_mutex_unlock(&fip_mfip.ba_mutex);
+        errno = INIT_NOT_RUNNING;
+        return -1;
+    }
+
+    if(fip_mfip.ba.is_running)
+    {
+        pthread_mutex_unlock(&fip_mfip.ba_mutex);
+        errno = INIT_ALREADY_RUNNING;
+        return -1;
+    }
+
+    if (mstrfip_ba_load(fip_mfip.dev, fip_mfip.ba.mcycle) < 0)
+    {
+        pthread_mutex_unlock(&fip_mfip.ba_mutex);
+        fipMfipStopProtocol();
+        errno = INIT_FDM_BA_FAILED;
+	return -1;
+    }
+
+    if (mstrfip_ba_start(fip_mfip.dev) < 0)
+    {
+        pthread_mutex_unlock(&fip_mfip.ba_mutex);
+        fipMfipStopProtocol();
+        errno = INIT_FDM_BA_FAILED;
+	return -1;
+    }
+    fip_mfip.ba.is_running = true;
+
+    // Reset the cycle count
+
+    fip_mfip.cycle_count = 0;
+
+    // Callback to indicate BA macrocycle program is running
+
+    if(fip_mfip.config.ba_up_func != NULL)
+    {
+        fip_mfip.config.ba_up_func();
+    }
+
+    // Set the status of the front-end and the diagnostic device to present on the WorldFIP bus
+
+    fipMfipSetDevicePresent(0);
+
+    errno = INIT_SUCCESS;
+
+    pthread_mutex_unlock(&fip_mfip.ba_mutex);
+
+    return 0;
+}
+
+
+
+void fipMfipStopProtocol()
+{
+    if(!fip_mfip.is_initialised) return;
+
+    // Protect starting and stopping
+
+    pthread_mutex_lock(&fip_mfip.ba_mutex);
+
+    // Stop the bus arbitrator
+    mstrfip_ba_reset(fip_mfip.dev);
+    fip_mfip.ba.is_running = false;
+
+    // Callback to indicate BA macrocycle program has stopped running
+
+    if(fip_mfip.config.ba_down_func != NULL)
+    {
+        fip_mfip.config.ba_down_func();
+    }
+
+    // Set the status of the front-end and the diagnostic device to not present on the WorldFIP bus
+
+    fipMfipSetDeviceNotPresent(0);
+
+    pthread_mutex_unlock(&fip_mfip.ba_mutex);
+}
+
+
+
+int32_t fipMfipRestartProtocol(void)
+{
+    int32_t result;
+    int32_t cancel_state_orig;
+
+    static pthread_mutex_t restart_mutex = PTHREAD_MUTEX_INITIALIZER;
+
+    pthread_setcancelstate(PTHREAD_CANCEL_DISABLE, &cancel_state_orig);
+    pthread_mutex_lock(&restart_mutex);
+
+    fipMfipStopProtocol();
+    result = fipMfipStartProtocol();
+
+    pthread_mutex_unlock(&restart_mutex);
+    pthread_setcancelstate(cancel_state_orig, &cancel_state_orig);
+
+    return result;
+}
+
+
+
+void fipMfipWriteVar(uint32_t id_dat)
+{
+    uint32_t idx;
+
+    if(!fip_mfip.ba.is_running)
+    {
+        fipMfipLogError("Attempt to write FIP variable while BA program is not running.\n");
+        return;
+    }
+
+    // Find the variable with this ID
+
+    for(idx = 0; idx < fip_mfip.ba.n_prod_per_var; ++idx)
+    {
+        if(fip_mfip.ba.prod_per_varlist[idx]->id == (int)id_dat) break;
+    }
+
+    // Validate
+
+    if(fip_mfip.ba.prod_per_varlist[idx]->id != (int)id_dat)
+    {
+        fipMfipLogError("Variable not found with ID_DAT = 0x%x.\n", id_dat);
+        return;
+    }
+
+    // Write the variable
+
+    if ( (fip_mfip.config.fieldbus_stats->error_flag == 0) &&
+	 (mstrfip_var_write(fip_mfip.dev, fip_mfip.ba.prod_per_varlist[idx]) != 0) )
+    {
+        fipMfipLogError("FIP Failed to write producer variable %u\n", idx);
+        fip_mfip.config.fieldbus_stats->error_flag = 1;
+    }
+}
+
+
+
+void fipMfipWriteVars(void)
+{
+    if(!fip_mfip.ba.is_running)
+    {
+        fipMfipLogError("Attempt to write FIP variables while BA program is not running.\n");
+        return;
+    }
+
+    for(uint32_t idx = 0; idx < fip_mfip.ba.n_prod_per_var; ++idx)
+    {
+        if ( (fip_mfip.config.fieldbus_stats->error_flag == 0) &&
+	     (mstrfip_var_write(fip_mfip.dev, fip_mfip.ba.prod_per_varlist[idx]) != 0) )
+        {
+            fipMfipLogError("FIP Failed to write producer variable %u\n", idx);
+            fip_mfip.config.fieldbus_stats->error_flag = 1;
+        }
+    }
+}
+
+
+
+bool fipMfipReadVar(struct mstrfip_data *data, FIP_device_stats *error_stats)
+{
+    if(!fip_mfip.ba.is_running)
+    {
+        fipMfipLogError("Attempt to read FIP variables while BA program is not running.\n");
+        return false;
+    }
+
+    mstrfip_var_update(fip_mfip.dev, data); // update the periodic status variable
+
+    switch (data->status) {
+        case MSTRFIP_DATA_OK:
+            error_stats->var_recv_count++;
+            fip_mfip.config.fieldbus_stats->errors.var_recv_count++;
+            return true;
+
+        case MSTRFIP_DATA_PAYLOAD_ERROR:
+            if(data->payload_error & MSTRFIP_FRAME_PAYLOAD_NOT_SIGNIFICANT) {
+                error_stats->nonsignificance_fault_count++;
+                fip_mfip.config.fieldbus_stats->errors.nonsignificance_fault_count++;
+                error_stats->significance_fault_count++;
+                fip_mfip.config.fieldbus_stats->errors.significance_fault_count++;
+    	    }
+	    if(data->payload_error & MSTRFIP_FRAME_PAYLOAD_NOT_REFRESH) {
+                error_stats->promptness_fail_count++;
+                fip_mfip.config.fieldbus_stats->errors.promptness_fail_count++;
+            }
+            break;
+
+        case MSTRFIP_DATA_FRAME_ERROR:
+    	    if (data->frame_error == MSTRFIP_FRAME_TMO) {
+                // A timeout is ignored and handled at applicaiton level
+    	    }
+	       else {
+                error_stats->user_error_count++;
+                fip_mfip.config.fieldbus_stats->errors.user_error_count++;
+            }
+	    break;
+	default:
+	    break;
+    }
+
+    return false;
+}
+
+
+
+bool fipMfipIsInterfaceUp(void)
+{
+    return fip_mfip.is_running;
+}
+
+
+
+uint32_t fipMfipGetStartTime(void)
+{
+    return fip_mfip.config.fieldbus_stats->start_time;
+}
+
+
+
+uint32_t fipMfipCycleTick(void)
+{
+    return ++fip_mfip.cycle_count;
+}
+
+
+
+uint32_t fipMfipGetCycleCount(void)
+{
+    return fip_mfip.cycle_count;
+}
+
+
+
+void fipMfipConfigureDevice(uint8_t address)
+{
+    fip_mfip.device_configured[address/8] |= 1 << (address % 8);
+}
+
+void fipMfipUnconfigureDevice(uint8_t address)
+{
+    fip_mfip.device_configured[address/8] &= ~(1 << (address % 8));
+}
+
+
+void fipMfipGetAllConfigured(uint8_t *ptr)
+{
+    memcpy(ptr, fip_mfip.device_configured, 32);
+}
+
+
+
+bool fipMfipIsDeviceConfigured(uint8_t address)
+{
+    return fip_mfip.device_configured[address/8] & (1 << (address % 8));
+}
+
+
+void fipMfipSetDevicePresent(uint8_t address)
+{
+    fip_mfip.device_present[address/8] |= 1 << (address % 8);
+}
+
+
+
+void fipMfipSetDeviceNotPresent(uint8_t address)
+{
+    fip_mfip.device_present[address/8] &= ~(1 << (address % 8));
+}
+
+
+
+bool fipMfipIsDevicePresent(uint8_t address)
+{
+    return fip_mfip.device_present[address/8] & (1 << (address % 8));
+}
+
+
+
+void fipMfipGetAllPresent(uint8_t *ptr)
+{
+    memcpy(ptr, fip_mfip.device_present, 32);
+}
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/inc/jtag.h b/tools/nfipjtag/nfipjtag/inc/jtag.h
new file mode 100644
index 0000000000000000000000000000000000000000..c8683b9ca91680ad102dc71a5ae1f200f6b21f71
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/inc/jtag.h
@@ -0,0 +1,64 @@
+/*!
+ * @file   jtag.h
+ * @brief  WorldFIP JTAG programmer
+ * @author Stephen Page
+ * @author Michael Davis
+ */
+
+#ifndef JTAG_H
+#define JTAG_H
+
+#include <stdbool.h>
+
+// Global variables
+
+extern bool quiet_mode;
+extern bool debug_mode;
+extern bool fast_mode;
+
+// Types of input file
+
+enum jtag_file_type
+{
+    JTAG_FILE_UNSET,
+    JTAG_FILE_SVF,
+    JTAG_FILE_XSVF
+};
+
+// External functions
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Perform JTAG programming
+ *
+ * @param[in] filename    Name of the file to program
+ * @param[in] type        Specify if the file is in SVF or XSVF format
+ *
+ * @retval  0    Programming completed successfully
+ * @retval -1    An error occurred
+ */
+
+int jtagProg(char *filename, enum jtag_file_type type);
+
+
+
+/*!
+ * Signal handler.
+ *
+ * When the JTAG programmer receives a signal, it sets a flag indicating that jtagProg should clean up and exit.
+ *
+ * @param[in] signal    Signal number
+ */
+
+void jtagSignalHandler(int signal);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/inc/nfipjtag_cycle.h b/tools/nfipjtag/nfipjtag/inc/nfipjtag_cycle.h
new file mode 100644
index 0000000000000000000000000000000000000000..d73bc5a68b7aec48d070b177594f315bba8ca884
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/inc/nfipjtag_cycle.h
@@ -0,0 +1,176 @@
+/*!
+ * @file   nfipjtag_cycle.h
+ * @brief  Define the BA program to reprogram FPGAs attached to a nanoFIP
+ * @author Michael Davis
+ */
+
+#ifndef __NFIPJTAG_CYCLE_H
+#define __NFIPJTAG_CYCLE_H
+
+#include <stdint.h>
+#include <stdbool.h>
+#include <mqueue.h>
+
+
+
+// Types
+
+/*!
+ * Values for TRST reset
+ */
+
+enum Nfipjtag_TRST_value
+{
+    TRST_LOW  = 0xA5,
+    TRST_HIGH = 0xDB
+};
+
+
+
+// Structures for Nfipjtag command and reception variables
+
+/*!
+ * TRST command
+ */
+
+struct __attribute__((__packed__)) Nfipjtag_TRST_command
+{
+    uint8_t low_high;
+};
+
+
+
+/*!
+ * Transmission buffer for JTAG data
+ */
+
+struct __attribute__((__packed__)) Nfipjtag_tx_var
+{
+    uint16_t length_bits;       //!< Length of data within jtag_bits
+    uint8_t  jtag_bits[122];    //!< Encoded JTAG data
+};
+
+
+
+/*!
+ * Nfipjtag reception variable
+ */
+
+struct __attribute__((__packed__)) Nfipjtag_rx_var
+{
+    uint8_t tdo;            //!< TDO (least significant bit)
+    uint8_t nfip_status;    //!< nanoFIP status
+};
+
+
+
+/*!
+ * nanoFIP reset variable
+ */
+
+struct __attribute__((__packed__)) Nfipjtag_nanofip_reset
+{
+    char addr[2];    //!< Byte 0 is address of device to soft reset. Byte 1 is address of device to power cycle.
+};
+
+
+
+/*!
+ * Transmit queue item
+ */
+
+struct Nfipjtag_tx_queue_item
+{
+    uint32_t               delay_us;      //!< Number of microseconds to delay without sending data
+    bool                   rx_request;    //!< Flag to indicate the request of received data
+
+    struct Nfipjtag_tx_var payload;       //!< Data to be transmitted across the FIP bus
+};
+
+
+
+/*!
+ * Structure for queue descriptors
+ */
+
+struct Nfipjtag_queues
+{
+    mqd_t rx_queue;    //!< Queue to receive FIP variables
+    mqd_t tx_queue;    //!< Queue to transmit FIP variables
+};
+
+
+
+/*!
+ * Queue descriptors
+ */
+
+extern struct Nfipjtag_queues nfipjtag_q;
+
+
+
+// External function declarations with C linkage
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*!
+ * Set TRST high or low.
+ *
+ * This function must be called with the FIP interface up but with no BA macrocycle program running.
+ *
+ * On FGClite, TRST is active low, to protect against radiation-induced Single Event Upsets (SEUs).
+ * TRST must be set high before reprogramming using JTAG, and set low again afterwards. This program
+ * runs in its own FIP bus cycle, so it can't be called while the main FIP BA is running.
+ *
+ * Note that there is no way to read the TRST value back from the device, so it is not possible to be
+ * sure that the command succeeded. However, if TRST is low, it is not possible to reprogram the device
+ * and if it is high, it is not possible to do a remote power cycle.
+ *
+ * @param[in] address    FIP address of the device to set
+ * @param[in] value      Value to set: TRST_LOW or TRST_HIGH
+ *
+ * @retval  0    The command probably succeeded (at least, it did not encounter an error)
+ * @retval -1    The command failed
+ */
+
+int32_t nfipjtagCycleSetTRST(uint8_t address, enum Nfipjtag_TRST_value value);
+
+
+
+/*!
+ * Initialise the inter-process communication for JTAG programming.
+ */
+
+int32_t nfipjtagCycleInit(void);
+
+
+
+/*!
+ * Clean up inter-process communication.
+ */
+
+void nfipjtagCycleCleanUp(void);
+
+
+
+/*!
+ * Initialise and start the JTAG FIP BA macrocycle program.
+ *
+ * @param[in] address      WorldFIP address of the device to be reprogrammed
+ * @param[in] bus_speed    Set to 0=31.25 Kb/s, 1=1000 Kb/s or 2=2500 Kb/s
+ *
+ * @retval  0    The BA was started successfully
+ * @retval -1    Starting the BA failed
+ */
+
+int32_t nfipjtagCycleStartProtocol(uint8_t address, uint8_t bus_speed);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/libxsvf.h b/tools/nfipjtag/nfipjtag/libxsvf/libxsvf.h
new file mode 100644
index 0000000000000000000000000000000000000000..1753774d385293a684a80ac046e6bb162c933166
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/libxsvf.h
@@ -0,0 +1,146 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#ifndef LIBXSVF_H
+#define LIBXSVF_H
+
+enum libxsvf_mode {
+	LIBXSVF_MODE_SVF = 1,
+	LIBXSVF_MODE_XSVF = 2,
+	LIBXSVF_MODE_SCAN = 3
+};
+
+enum libxsvf_tap_state {
+	/* Special States */
+	LIBXSVF_TAP_INIT = 0,
+	LIBXSVF_TAP_RESET = 1,
+	LIBXSVF_TAP_IDLE = 2,
+	/* DR States */
+	LIBXSVF_TAP_DRSELECT = 3,
+	LIBXSVF_TAP_DRCAPTURE = 4,
+	LIBXSVF_TAP_DRSHIFT = 5,
+	LIBXSVF_TAP_DREXIT1 = 6,
+	LIBXSVF_TAP_DRPAUSE = 7,
+	LIBXSVF_TAP_DREXIT2 = 8,
+	LIBXSVF_TAP_DRUPDATE = 9,
+	/* IR States */
+	LIBXSVF_TAP_IRSELECT = 10,
+	LIBXSVF_TAP_IRCAPTURE = 11,
+	LIBXSVF_TAP_IRSHIFT = 12,
+	LIBXSVF_TAP_IREXIT1 = 13,
+	LIBXSVF_TAP_IRPAUSE = 14,
+	LIBXSVF_TAP_IREXIT2 = 15,
+	LIBXSVF_TAP_IRUPDATE = 16
+};
+
+enum libxsvf_mem {
+	LIBXSVF_MEM_XSVF_TDI_DATA = 0,
+	LIBXSVF_MEM_XSVF_TDO_DATA = 1,
+	LIBXSVF_MEM_XSVF_TDO_MASK = 2,
+	LIBXSVF_MEM_XSVF_ADDR_MASK = 3,
+	LIBXSVF_MEM_XSVF_DATA_MASK = 4,
+	LIBXSVF_MEM_SVF_COMMANDBUF = 5,
+	LIBXSVF_MEM_SVF_SDR_TDI_DATA = 6,
+	LIBXSVF_MEM_SVF_SDR_TDI_MASK = 7,
+	LIBXSVF_MEM_SVF_SDR_TDO_DATA = 8,
+	LIBXSVF_MEM_SVF_SDR_TDO_MASK = 9,
+	LIBXSVF_MEM_SVF_SDR_RET_MASK = 10,
+	LIBXSVF_MEM_SVF_SIR_TDI_DATA = 11,
+	LIBXSVF_MEM_SVF_SIR_TDI_MASK = 12,
+	LIBXSVF_MEM_SVF_SIR_TDO_DATA = 13,
+	LIBXSVF_MEM_SVF_SIR_TDO_MASK = 14,
+	LIBXSVF_MEM_SVF_SIR_RET_MASK = 15,
+	LIBXSVF_MEM_SVF_HDR_TDI_DATA = 16,
+	LIBXSVF_MEM_SVF_HDR_TDI_MASK = 17,
+	LIBXSVF_MEM_SVF_HDR_TDO_DATA = 18,
+	LIBXSVF_MEM_SVF_HDR_TDO_MASK = 19,
+	LIBXSVF_MEM_SVF_HDR_RET_MASK = 20,
+	LIBXSVF_MEM_SVF_HIR_TDI_DATA = 21,
+	LIBXSVF_MEM_SVF_HIR_TDI_MASK = 22,
+	LIBXSVF_MEM_SVF_HIR_TDO_DATA = 23,
+	LIBXSVF_MEM_SVF_HIR_TDO_MASK = 24,
+	LIBXSVF_MEM_SVF_HIR_RET_MASK = 25,
+	LIBXSVF_MEM_SVF_TDR_TDI_DATA = 26,
+	LIBXSVF_MEM_SVF_TDR_TDI_MASK = 27,
+	LIBXSVF_MEM_SVF_TDR_TDO_DATA = 28,
+	LIBXSVF_MEM_SVF_TDR_TDO_MASK = 29,
+	LIBXSVF_MEM_SVF_TDR_RET_MASK = 30,
+	LIBXSVF_MEM_SVF_TIR_TDI_DATA = 31,
+	LIBXSVF_MEM_SVF_TIR_TDI_MASK = 32,
+	LIBXSVF_MEM_SVF_TIR_TDO_DATA = 33,
+	LIBXSVF_MEM_SVF_TIR_TDO_MASK = 34,
+	LIBXSVF_MEM_SVF_TIR_RET_MASK = 35,
+	LIBXSVF_MEM_NUM = 36
+};
+
+struct libxsvf_host {
+	int (*setup)(struct libxsvf_host *h);
+	int (*shutdown)(struct libxsvf_host *h);
+	void (*udelay)(struct libxsvf_host *h, long usecs, int tms, long num_tck);
+	int (*getbyte)(struct libxsvf_host *h);
+	int (*sync)(struct libxsvf_host *h);
+	int (*pulse_tck)(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync);
+	void (*pulse_sck)(struct libxsvf_host *h);
+	void (*set_trst)(struct libxsvf_host *h, int v);
+	int (*set_frequency)(struct libxsvf_host *h, int v);
+	void (*report_tapstate)(struct libxsvf_host *h);
+	void (*report_device)(struct libxsvf_host *h, unsigned long idcode);
+	void (*report_status)(struct libxsvf_host *h, const char *message);
+	void (*report_error)(struct libxsvf_host *h, const char *file, int line, const char *message);
+	void *(*realloc)(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which);
+	enum libxsvf_tap_state tap_state;
+	void *user_data;
+};
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+int libxsvf_play(struct libxsvf_host *, enum libxsvf_mode mode);
+const char *libxsvf_state2str(enum libxsvf_tap_state tap_state);
+const char *libxsvf_mem2str(enum libxsvf_mem which);
+
+/* Internal API */ 
+int libxsvf_svf(struct libxsvf_host *h);
+int libxsvf_xsvf(struct libxsvf_host *h);
+int libxsvf_scan(struct libxsvf_host *h);
+int libxsvf_tap_walk(struct libxsvf_host *, enum libxsvf_tap_state);
+
+#ifdef __cplusplus
+}
+#endif
+
+/* Host accessor macros (see README) */
+#define LIBXSVF_HOST_SETUP() h->setup(h)
+#define LIBXSVF_HOST_SHUTDOWN() h->shutdown(h)
+#define LIBXSVF_HOST_UDELAY(_usecs, _tms, _num_tck) h->udelay(h, _usecs, _tms, _num_tck)
+#define LIBXSVF_HOST_GETBYTE() h->getbyte(h)
+#define LIBXSVF_HOST_SYNC() (h->sync ? h->sync(h) : 0)
+#define LIBXSVF_HOST_PULSE_TCK(_tms, _tdi, _tdo, _rmask, _sync) h->pulse_tck(h, _tms, _tdi, _tdo, _rmask, _sync)
+#define LIBXSVF_HOST_PULSE_SCK() do { if (h->pulse_sck) h->pulse_sck(h); } while (0)
+#define LIBXSVF_HOST_SET_TRST(_v) do { if (h->set_trst) h->set_trst(h, _v); } while (0)
+#define LIBXSVF_HOST_SET_FREQUENCY(_v) (h->set_frequency ? h->set_frequency(h, _v) : -1)
+#define LIBXSVF_HOST_REPORT_TAPSTATE() do { if (h->report_tapstate) h->report_tapstate(h); } while (0)
+#define LIBXSVF_HOST_REPORT_DEVICE(_v) do { if (h->report_device) h->report_device(h, _v); } while (0)
+#define LIBXSVF_HOST_REPORT_STATUS(_msg) do { if (h->report_status) h->report_status(h, _msg); } while (0)
+#define LIBXSVF_HOST_REPORT_ERROR(_msg) h->report_error(h, __FILE__, __LINE__, _msg)
+#define LIBXSVF_HOST_REALLOC(_ptr, _size, _which) h->realloc(h, _ptr, _size, _which)
+
+#endif
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/memname.c b/tools/nfipjtag/nfipjtag/libxsvf/memname.c
new file mode 100644
index 0000000000000000000000000000000000000000..6d8207da9ef52aefde8bbd7f7f77258e230f9061
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/memname.c
@@ -0,0 +1,64 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+const char *libxsvf_mem2str(enum libxsvf_mem which)
+{
+#define X(_w, _t) if (which == LIBXSVF_MEM_ ## _w) return #_t;
+	X(XSVF_TDI_DATA, xsvf_tdi_data)
+	X(XSVF_TDO_DATA, xsvf_tdo_data)
+	X(XSVF_TDO_MASK, xsvf_tdo_mask)
+	X(XSVF_ADDR_MASK, xsvf_addr_mask)
+	X(XSVF_DATA_MASK, xsvf_data_mask)
+	X(SVF_COMMANDBUF, svf_commandbuf)
+	X(SVF_HDR_TDI_DATA, svf_hdr_tdi_data)
+	X(SVF_HDR_TDI_MASK, svf_hdr_tdi_mask)
+	X(SVF_HDR_TDO_DATA, svf_hdr_tdo_data)
+	X(SVF_HDR_TDO_MASK, svf_hdr_tdo_mask)
+	X(SVF_HDR_RET_MASK, svf_hdr_ret_mask)
+	X(SVF_HIR_TDI_DATA, svf_hir_tdi_data)
+	X(SVF_HIR_TDI_MASK, svf_hir_tdi_mask)
+	X(SVF_HIR_TDO_DATA, svf_hir_tdo_data)
+	X(SVF_HIR_TDO_MASK, svf_hir_tdo_mask)
+	X(SVF_HIR_RET_MASK, svf_hir_ret_mask)
+	X(SVF_TDR_TDI_DATA, svf_tdr_tdi_data)
+	X(SVF_TDR_TDI_MASK, svf_tdr_tdi_mask)
+	X(SVF_TDR_TDO_DATA, svf_tdr_tdo_data)
+	X(SVF_TDR_TDO_MASK, svf_tdr_tdo_mask)
+	X(SVF_TDR_RET_MASK, svf_tdr_ret_mask)
+	X(SVF_TIR_TDI_DATA, svf_tir_tdi_data)
+	X(SVF_TIR_TDI_MASK, svf_tir_tdi_mask)
+	X(SVF_TIR_TDO_DATA, svf_tir_tdo_data)
+	X(SVF_TIR_TDO_MASK, svf_tir_tdo_mask)
+	X(SVF_TIR_RET_MASK, svf_tir_ret_mask)
+	X(SVF_SDR_TDI_DATA, svf_sdr_tdi_data)
+	X(SVF_SDR_TDI_MASK, svf_sdr_tdi_mask)
+	X(SVF_SDR_TDO_DATA, svf_sdr_tdo_data)
+	X(SVF_SDR_TDO_MASK, svf_sdr_tdo_mask)
+	X(SVF_SDR_RET_MASK, svf_sdr_ret_mask)
+	X(SVF_SIR_TDI_DATA, svf_sir_tdi_data)
+	X(SVF_SIR_TDI_MASK, svf_sir_tdi_mask)
+	X(SVF_SIR_TDO_DATA, svf_sir_tdo_data)
+	X(SVF_SIR_TDO_MASK, svf_sir_tdo_mask)
+	X(SVF_SIR_RET_MASK, svf_sir_ret_mask)
+#undef X
+	return (void*)0;
+}
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/play.c b/tools/nfipjtag/nfipjtag/libxsvf/play.c
new file mode 100644
index 0000000000000000000000000000000000000000..6dc295751bdeb4ed2e9ef8bc76da1f1eb23c760e
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/play.c
@@ -0,0 +1,71 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+int libxsvf_play(struct libxsvf_host *h, enum libxsvf_mode mode)
+{
+	int rc = -1;
+
+	h->tap_state = LIBXSVF_TAP_INIT;
+	if (LIBXSVF_HOST_SETUP() < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("Setup of JTAG interface failed.");
+		return -1;
+	}
+
+	if (mode == LIBXSVF_MODE_SVF) {
+#ifdef LIBXSVF_WITHOUT_SVF
+		LIBXSVF_HOST_REPORT_ERROR("SVF support in libxsvf is disabled.");
+#else
+		rc = libxsvf_svf(h);
+#endif
+	}
+
+	if (mode == LIBXSVF_MODE_XSVF) {
+#ifdef LIBXSVF_WITHOUT_XSVF
+		LIBXSVF_HOST_REPORT_ERROR("XSVF support in libxsvf is disabled.");
+#else
+		rc = libxsvf_xsvf(h);
+#endif
+	}
+
+	if (mode == LIBXSVF_MODE_SCAN) {
+#ifdef LIBXSVF_WITHOUT_SCAN
+		LIBXSVF_HOST_REPORT_ERROR("SCAN support in libxsvf is disabled.");
+#else
+		rc = libxsvf_scan(h);
+#endif
+	}
+
+	libxsvf_tap_walk(h, LIBXSVF_TAP_RESET);
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch in TAP reset. (this is not possible!)");
+		rc = -1;
+	}
+
+	int shutdown_rc = LIBXSVF_HOST_SHUTDOWN();
+
+	if (shutdown_rc < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("Shutdown of JTAG interface failed.");
+		rc = rc < 0 ? rc : shutdown_rc;
+	}
+
+	return rc;
+}
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/scan.c b/tools/nfipjtag/nfipjtag/libxsvf/scan.c
new file mode 100644
index 0000000000000000000000000000000000000000..a14fb681bdba9a5cfc768b71f1933ff12a730064
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/scan.c
@@ -0,0 +1,57 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+int libxsvf_scan(struct libxsvf_host *h)
+{
+	int i, j;
+
+	if (libxsvf_tap_walk(h, LIBXSVF_TAP_RESET) < 0)
+		return -1;
+
+	if (libxsvf_tap_walk(h, LIBXSVF_TAP_DRSHIFT) < 0)
+		return -1;
+
+	for (i=0; i<256; i++)
+	{
+		int bit = LIBXSVF_HOST_PULSE_TCK(0, 1, -1, 0, 1);
+
+		if (bit < 0)
+			return -1;
+
+		if (bit == 0) {
+			LIBXSVF_HOST_REPORT_DEVICE(0);
+		} else {
+			unsigned long idcode = 1;
+			for (j=1; j<32; j++) {
+				int bit = LIBXSVF_HOST_PULSE_TCK(0, 1, -1, 0, 1);
+				if (bit < 0)
+					return -1;
+				idcode |= ((unsigned long)bit) << j;
+			}
+			if (idcode == 0xffffffff)
+				break;
+			LIBXSVF_HOST_REPORT_DEVICE(idcode);
+		}
+	}
+
+	return 0;
+}
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/statename.c b/tools/nfipjtag/nfipjtag/libxsvf/statename.c
new file mode 100644
index 0000000000000000000000000000000000000000..9b1b47bc377fec3700abd153ffca8879788190eb
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/statename.c
@@ -0,0 +1,45 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+const char *libxsvf_state2str(enum libxsvf_tap_state tap_state)
+{
+#define X(_s) if (tap_state == _s) return #_s;
+	X(LIBXSVF_TAP_INIT)
+	X(LIBXSVF_TAP_RESET)
+	X(LIBXSVF_TAP_IDLE)
+	X(LIBXSVF_TAP_DRSELECT)
+	X(LIBXSVF_TAP_DRCAPTURE)
+	X(LIBXSVF_TAP_DRSHIFT)
+	X(LIBXSVF_TAP_DREXIT1)
+	X(LIBXSVF_TAP_DRPAUSE)
+	X(LIBXSVF_TAP_DREXIT2)
+	X(LIBXSVF_TAP_DRUPDATE)
+	X(LIBXSVF_TAP_IRSELECT)
+	X(LIBXSVF_TAP_IRCAPTURE)
+	X(LIBXSVF_TAP_IRSHIFT)
+	X(LIBXSVF_TAP_IREXIT1)
+	X(LIBXSVF_TAP_IRPAUSE)
+	X(LIBXSVF_TAP_IREXIT2)
+	X(LIBXSVF_TAP_IRUPDATE)
+#undef X
+	return "UNKOWN_STATE";
+}
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/svf.c b/tools/nfipjtag/nfipjtag/libxsvf/svf.c
new file mode 100644
index 0000000000000000000000000000000000000000..aa7a374521816021152c3732d79090b831093f76
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/svf.c
@@ -0,0 +1,659 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+static int read_command(struct libxsvf_host *h, char **buffer_p, int *len_p)
+{
+	char *buffer = *buffer_p;
+	int braket_mode = 0;
+	int len = *len_p;
+	int p = 0;
+
+	while (1)
+	{
+		if (len < p+10) {
+			len = len < 64 ? 96 : len*2;
+			buffer = LIBXSVF_HOST_REALLOC(buffer, len, LIBXSVF_MEM_SVF_COMMANDBUF);
+			*buffer_p = buffer;
+			*len_p = len;
+			if (!buffer) {
+				LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+				return -1;
+			}
+		}
+		buffer[p] = 0;
+
+		int ch = LIBXSVF_HOST_GETBYTE();
+		if (ch < 0) {
+handle_eof:
+			if (p == 0)
+				return 0;
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");
+			return -1;
+		}
+		if (ch <= ' ') {
+insert_eol:
+			if (!braket_mode && p > 0 && buffer[p-1] != ' ')
+				buffer[p++] = ' ';
+			continue;
+		}
+		if (ch == '!') {
+skip_to_eol:
+			while (1) {
+				ch = LIBXSVF_HOST_GETBYTE();
+				if (ch < 0)
+					goto handle_eof;
+				if (ch < ' ' && ch != '\t')
+					goto insert_eol;
+			}
+		}
+		if (ch == '/' && p > 0 && buffer[p-1] == '/') {
+			p--;
+			goto skip_to_eol;
+		}
+		if (ch == ';')
+			break;
+		if (ch == '(') {
+			if (!braket_mode && p > 0 && buffer[p-1] != ' ')
+				buffer[p++] = ' ';
+			braket_mode++;
+		}
+		if (ch >= 'a' && ch <= 'z')
+			buffer[p++] = ch - ('a' - 'A');
+		else
+			buffer[p++] = ch;
+		if (ch == ')') {
+			braket_mode--;
+			if (!braket_mode)
+				buffer[p++] = ' ';
+		}
+	}
+	return 1;
+}
+
+static int strtokencmp(const char *str1, const char *str2)
+{
+	int i = 0;
+	while (1) {
+		if ((str1[i] == ' ' || str1[i] == 0) && (str2[i] == ' ' || str2[i] == 0))
+			return 0;
+		if (str1[i] < str2[i])
+			return -1;
+		if (str1[i] > str2[i])
+			return +1;
+		i++;
+	}
+}
+
+static int strtokenskip(const char *str1)
+{
+	int i = 0;
+	while (str1[i] != 0 && str1[i] != ' ') i++;
+	while (str1[i] == ' ') i++;
+	return i;
+}
+
+static int token2tapstate(const char *str1)
+{
+#define X(_t) if (!strtokencmp(str1, #_t)) return LIBXSVF_TAP_ ## _t;
+	X(RESET)
+	X(IDLE)
+	X(DRSELECT)
+	X(DRCAPTURE)
+	X(DRSHIFT)
+	X(DREXIT1)
+	X(DRPAUSE)
+	X(DREXIT2)
+	X(DRUPDATE)
+	X(IRSELECT)
+	X(IRCAPTURE)
+	X(IRSHIFT)
+	X(IREXIT1)
+	X(IRPAUSE)
+	X(IREXIT2)
+	X(IRUPDATE)
+#undef X
+	return -1;
+}
+
+struct bitdata_s {
+	int len, alloced_len;
+	int alloced_bytes;
+	unsigned char *tdi_data;
+	unsigned char *tdi_mask;
+	unsigned char *tdo_data;
+	unsigned char *tdo_mask;
+	unsigned char *ret_mask;
+	int has_tdo_data;
+};
+
+static void bitdata_free(struct libxsvf_host *h, struct bitdata_s *bd, int offset)
+{
+	LIBXSVF_HOST_REALLOC(bd->tdi_data, 0, offset+0);
+	LIBXSVF_HOST_REALLOC(bd->tdi_mask, 0, offset+1);
+	LIBXSVF_HOST_REALLOC(bd->tdo_data, 0, offset+2);
+	LIBXSVF_HOST_REALLOC(bd->tdo_mask, 0, offset+3);
+	LIBXSVF_HOST_REALLOC(bd->ret_mask, 0, offset+4);
+
+	bd->tdi_data = (void*)0;
+	bd->tdi_mask = (void*)0;
+	bd->tdo_data = (void*)0;
+	bd->tdo_mask = (void*)0;
+	bd->ret_mask = (void*)0;
+}
+
+static int hex(char ch)
+{
+	if (ch >= 'A' && ch <= 'Z')
+		return (ch - 'A') + 10;
+	if (ch >= '0' && ch <= '9')
+		return ch - '0';
+	return 0;
+}
+
+static const char *bitdata_parse(struct libxsvf_host *h, const char *p, struct bitdata_s *bd, int offset)
+{
+	int i, j;
+	bd->len = 0;
+	bd->has_tdo_data = 0;
+	while (*p >= '0' && *p <= '9') {
+		bd->len = bd->len * 10 + (*p - '0');
+		p++;
+	}
+	while (*p == ' ') {
+		p++;
+	}
+	if (bd->len != bd->alloced_len) {
+		bitdata_free(h, bd, offset);
+		bd->alloced_len = bd->len;
+		bd->alloced_bytes = (bd->len+7) / 8;
+	}
+	while (*p)
+	{
+		int memnum = 0;
+		unsigned char **dp = (void*)0;
+		if (!strtokencmp(p, "TDI")) {
+			p += strtokenskip(p);
+			dp = &bd->tdi_data;
+			memnum = 0;
+		}
+		if (!strtokencmp(p, "TDO")) {
+			p += strtokenskip(p);
+			dp = &bd->tdo_data;
+			bd->has_tdo_data = 1;
+			memnum = 1;
+		}
+		if (!strtokencmp(p, "SMASK")) {
+			p += strtokenskip(p);
+			dp = &bd->tdi_mask;
+			memnum = 2;
+		}
+		if (!strtokencmp(p, "MASK")) {
+			p += strtokenskip(p);
+			dp = &bd->tdo_mask;
+			memnum = 3;
+		}
+		if (!strtokencmp(p, "RMASK")) {
+			p += strtokenskip(p);
+			dp = &bd->ret_mask;
+			memnum = 4;
+		}
+		if (!dp)
+			return (void*)0;
+		if (*dp == (void*)0) {
+			*dp = LIBXSVF_HOST_REALLOC(*dp, bd->alloced_bytes, offset+memnum);
+		}
+		if (*dp == (void*)0) {
+			LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+			return (void*)0;
+		}
+
+		unsigned char *d = *dp;
+		for (i=0; i<bd->alloced_bytes; i++)
+			d[i] = 0;
+
+		if (*p != '(')
+			return (void*)0;
+		p++;
+
+		int hexdigits = 0;
+		for (i=0; (p[i] >= 'A' && p[i] <= 'F') || (p[i] >= '0' && p[i] <= '9'); i++)
+			hexdigits++;
+
+		i = bd->alloced_bytes*2 - hexdigits;
+		for (j=0; j<hexdigits; j++, i++, p++) {
+			if (i%2 == 0) {
+				d[i/2] |= hex(*p) << 4;
+			} else {
+				d[i/2] |= hex(*p);
+			}
+		}
+
+		if (*p != ')')
+			return (void*)0;
+		p++;
+		while (*p == ' ') {
+			p++;
+		}
+	}
+#if 0
+	/* Debugging Output, needs <stdio.h> */
+	printf("--- Parsed bitdata [%d] ---\n", bd->len);
+	if (bd->tdi_data) {
+		printf("TDI DATA:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdi_data[i]);
+		printf("\n");
+	}
+	if (bd->tdo_data && has_tdo_data) {
+		printf("TDO DATA:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdo_data[i]);
+		printf("\n");
+	}
+	if (bd->tdi_mask) {
+		printf("TDI MASK:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdi_mask[i]);
+		printf("\n");
+	}
+	if (bd->tdo_mask) {
+		printf("TDO MASK:");
+		for (i=0; i<bd->alloced_bytes; i++)
+			printf(" %02x", bd->tdo_mask[i]);
+		printf("\n");
+	}
+#endif
+	return p;
+}
+
+static int getbit(unsigned char *data, int n)
+{
+	return (data[n/8] & (1 << (7 - n%8))) ? 1 : 0;
+}
+
+static int bitdata_play(struct libxsvf_host *h, struct bitdata_s *bd, enum libxsvf_tap_state estate)
+{
+	int left_padding = (8 - bd->len % 8) % 8;
+	int tdo_error = 0;
+	int tms = 0;
+	int i;
+
+	for (i=bd->len+left_padding-1; i >= left_padding; i--) {
+		if (i == left_padding && h->tap_state != estate) {
+			h->tap_state++;
+			tms = 1;
+		}
+		int tdi = -1;
+		if (bd->tdi_data) {
+			if (!bd->tdi_mask || getbit(bd->tdi_mask, i))
+				tdi = getbit(bd->tdi_data, i);
+		}
+		int tdo = -1;
+		if (bd->tdo_data && bd->has_tdo_data && (!bd->tdo_mask || getbit(bd->tdo_mask, i)))
+			tdo = getbit(bd->tdo_data, i);
+		int rmask = bd->ret_mask && getbit(bd->ret_mask, i);
+		if (LIBXSVF_HOST_PULSE_TCK(tms, tdi, tdo, rmask, 0) < 0)
+			tdo_error = 1;
+	}
+
+	if (tms)
+		LIBXSVF_HOST_REPORT_TAPSTATE();
+
+	if (!tdo_error)
+		return 0;
+
+	LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+	return -1;
+}
+
+int libxsvf_svf(struct libxsvf_host *h)
+{
+	char *command_buffer = (void*)0;
+	int command_buffer_len = 0;
+	int rc, i;
+
+	struct bitdata_s bd_hdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_hir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_tdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_tir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_sdr = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+	struct bitdata_s bd_sir = { 0, 0, 0, (void*)0, (void*)0, (void*)0, (void*)0, (void*)0 };
+
+	int state_endir = LIBXSVF_TAP_IDLE;
+	int state_enddr = LIBXSVF_TAP_IDLE;
+	int state_run = LIBXSVF_TAP_IDLE;
+	int state_endrun = LIBXSVF_TAP_IDLE;
+
+	while (1)
+	{
+		rc = read_command(h, &command_buffer, &command_buffer_len);
+
+		if (rc <= 0)
+			break;
+
+		const char *p = command_buffer;
+
+		LIBXSVF_HOST_REPORT_STATUS(command_buffer);
+
+		if (!strtokencmp(p, "ENDIR")) {
+			p += strtokenskip(p);
+			state_endir = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "ENDDR")) {
+			p += strtokenskip(p);
+			state_enddr = token2tapstate(p);
+			if (state_endir < 0)
+				goto syntax_error;
+			p += strtokenskip(p);
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "FREQUENCY")) {
+			unsigned long number = 0;
+			int exp = 0;
+			p += strtokenskip(p);
+			if (*p < '0' || *p > '9')
+				goto syntax_error;
+			while (*p >= '0' && *p <= '9') {
+				number = number*10 + (*p - '0');
+				p++;
+			}
+			if(*p == 'E' || *p == 'e') {
+				p++;
+				while (*p >= '0' && *p <= '9') {
+					exp = exp*10 + (*p - '0');
+					p++;
+				}
+				for(i=0; i<exp; i++)
+					number *= 10;
+			}
+			while (*p == ' ') {
+				p++;
+			}
+			p += strtokenskip(p);
+			if (LIBXSVF_HOST_SET_FREQUENCY(number) < 0) {
+				LIBXSVF_HOST_REPORT_ERROR("FREQUENCY command failed!");
+				goto error;
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "HIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "PIO") || !strtokencmp(p, "PIOMAP")) {
+			goto unsupported_error;
+		}
+
+		if (!strtokencmp(p, "RUNTEST")) {
+			p += strtokenskip(p);
+			int tck_count = -1;
+			int sck_count = -1;
+			int min_time = -1;
+			int max_time = -1;
+			while (*p) {
+				int got_maximum = 0;
+				if (!strtokencmp(p, "MAXIMUM")) {
+					p += strtokenskip(p);
+					got_maximum = 1;
+				}
+				int got_endstate = 0;
+				if (!strtokencmp(p, "ENDSTATE")) {
+					p += strtokenskip(p);
+					got_endstate = 1;
+				}
+				int st = token2tapstate(p);
+				if (st >= 0) {
+					p += strtokenskip(p);
+					if (got_endstate)
+						state_endrun = st;
+					else
+						state_run = st;
+					continue;
+				}
+				if (*p < '0' || *p > '9')
+					goto syntax_error;
+				int number = 0;
+				int exp = 0, expsign = 1;
+				int number_e6, exp_e6;
+				while (*p >= '0' && *p <= '9') {
+					number = number*10 + (*p - '0');
+					p++;
+				}
+				if(*p == 'E' || *p == 'e') {
+					p++;
+					if(*p == '-') {
+						expsign = -1;
+						p++;
+					}
+					while (*p >= '0' && *p <= '9') {
+						exp = exp*10 + (*p - '0');
+						p++;
+					}
+					exp = exp * expsign;
+					number_e6 = number;
+					exp_e6 = exp + 6;
+					while (exp < 0) {
+						number /= 10;
+						exp++;
+					}
+					while (exp > 0) {
+						number *= 10;
+						exp--;
+					}
+					while (exp_e6 < 0) {
+						number_e6 /= 10;
+						exp_e6++;
+					}
+					while (exp_e6 > 0) {
+						number_e6 *= 10;
+						exp_e6--;
+					}
+				} else {
+					number_e6 = number * 1000000;
+				}
+				while (*p == ' ') {
+					p++;
+				}
+				if (!strtokencmp(p, "SEC")) {
+					p += strtokenskip(p);
+					if (got_maximum)
+						max_time = number_e6;
+					else
+						min_time = number_e6;
+					continue;
+				}
+				if (!strtokencmp(p, "TCK")) {
+					p += strtokenskip(p);
+					tck_count = number;
+					continue;
+				}
+				if (!strtokencmp(p, "SCK")) {
+					p += strtokenskip(p);
+					sck_count = number;
+					continue;
+				}
+				goto syntax_error;
+			}
+			if (libxsvf_tap_walk(h, state_run) < 0)
+				goto error;
+			if (max_time >= 0) {
+				LIBXSVF_HOST_REPORT_ERROR("WARNING: Maximum time in SVF RUNTEST command is ignored.");
+			}
+			if (sck_count >= 0) {
+				for (i=0; i < sck_count; i++) {
+					LIBXSVF_HOST_PULSE_SCK();
+				}
+			}
+			if (min_time >= 0) {
+				LIBXSVF_HOST_UDELAY(min_time, 0, tck_count >= 0 ? tck_count : 0);
+			}
+			else if (tck_count >= 0) {
+				for (i=0; i < tck_count; i++) {
+					LIBXSVF_HOST_PULSE_TCK(0, -1, -1, 0, 0);
+				}
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_DRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hdr, bd_sdr.len+bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sdr, bd_tdr.len > 0 ? LIBXSVF_TAP_DRSHIFT : state_enddr) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tdr, state_enddr) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_enddr) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "SIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			if (libxsvf_tap_walk(h, LIBXSVF_TAP_IRSHIFT) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_hir, bd_sir.len+bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_sir, bd_tir.len > 0 ? LIBXSVF_TAP_IRSHIFT : state_endir) < 0)
+				goto error;
+			if (bitdata_play(h, &bd_tir, state_endir) < 0)
+				goto error;
+			if (libxsvf_tap_walk(h, state_endir) < 0)
+				goto error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "STATE")) {
+			p += strtokenskip(p);
+			while (*p) {
+				int st = token2tapstate(p);
+				if (st < 0)
+					goto syntax_error;
+				if (libxsvf_tap_walk(h, st) < 0)
+					goto error;
+				p += strtokenskip(p);
+			}
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TDR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TIR")) {
+			p += strtokenskip(p);
+			p = bitdata_parse(h, p, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+			if (!p)
+				goto syntax_error;
+			goto eol_check;
+		}
+
+		if (!strtokencmp(p, "TRST")) {
+			p += strtokenskip(p);
+			if (!strtokencmp(p, "ON")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "OFF")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(0);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "Z")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-1);
+				goto eol_check;
+			}
+			if (!strtokencmp(p, "ABSENT")) {
+				p += strtokenskip(p);
+				LIBXSVF_HOST_SET_TRST(-2);
+				goto eol_check;
+			}
+			goto syntax_error;
+		}
+
+eol_check:
+		while (*p == ' ')
+			p++;
+		if (*p == 0)
+			continue;
+
+syntax_error:
+		LIBXSVF_HOST_REPORT_ERROR("SVF Syntax Error:");
+		if (0) {
+unsupported_error:
+			LIBXSVF_HOST_REPORT_ERROR("Error in SVF input: unsupported command:");
+		}
+		LIBXSVF_HOST_REPORT_ERROR(command_buffer);
+error:
+		rc = -1;
+		break;
+	}
+
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		rc = -1;
+	}
+
+	bitdata_free(h, &bd_hdr, LIBXSVF_MEM_SVF_HDR_TDI_DATA);
+	bitdata_free(h, &bd_hir, LIBXSVF_MEM_SVF_HIR_TDI_DATA);
+	bitdata_free(h, &bd_tdr, LIBXSVF_MEM_SVF_TDR_TDI_DATA);
+	bitdata_free(h, &bd_tir, LIBXSVF_MEM_SVF_TIR_TDI_DATA);
+	bitdata_free(h, &bd_sdr, LIBXSVF_MEM_SVF_SDR_TDI_DATA);
+	bitdata_free(h, &bd_sir, LIBXSVF_MEM_SVF_SIR_TDI_DATA);
+
+	LIBXSVF_HOST_REALLOC(command_buffer, 0, LIBXSVF_MEM_SVF_COMMANDBUF);
+
+	return rc;
+}
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/tap.c b/tools/nfipjtag/nfipjtag/libxsvf/tap.c
new file mode 100644
index 0000000000000000000000000000000000000000..22b99518adb240a10879fc513f06d4f532eabaf3
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/tap.c
@@ -0,0 +1,173 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+static void tap_transition(struct libxsvf_host *h, int v)
+{
+	LIBXSVF_HOST_PULSE_TCK(v, -1, -1, 0, 0);
+}
+
+int libxsvf_tap_walk(struct libxsvf_host *h, enum libxsvf_tap_state s)
+{
+	int i, j;
+	for (i=0; s != h->tap_state; i++)
+	{
+		switch (h->tap_state)
+		{
+		/* Special States */
+		case LIBXSVF_TAP_INIT:
+			for (j = 0; j < 6; j++)
+				tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_RESET;
+			break;
+		case LIBXSVF_TAP_RESET:
+			tap_transition(h, 0);
+			h->tap_state = LIBXSVF_TAP_IDLE;
+			break;
+		case LIBXSVF_TAP_IDLE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DRSELECT;
+			break;
+
+		/* DR States */
+		case LIBXSVF_TAP_DRSELECT:
+			if (s >= LIBXSVF_TAP_IRSELECT || s == LIBXSVF_TAP_RESET) {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRSELECT;
+			} else {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRCAPTURE;
+			}
+			break;
+		case LIBXSVF_TAP_DRCAPTURE:
+			if (s == LIBXSVF_TAP_DRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DREXIT1;
+			}
+			break;
+		case LIBXSVF_TAP_DRSHIFT:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DREXIT1;
+			break;
+		case LIBXSVF_TAP_DREXIT1:
+			if (s == LIBXSVF_TAP_DRPAUSE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRPAUSE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_DRPAUSE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_DREXIT2;
+			break;
+		case LIBXSVF_TAP_DREXIT2:
+			if (s == LIBXSVF_TAP_DRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_DRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_DRUPDATE:
+			if (s == LIBXSVF_TAP_IDLE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IDLE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRSELECT;
+			}
+			break;
+
+		/* IR States */
+		case LIBXSVF_TAP_IRSELECT:
+			if (s == LIBXSVF_TAP_RESET) {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_RESET;
+			} else {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRCAPTURE;
+			}
+			break;
+		case LIBXSVF_TAP_IRCAPTURE:
+			if (s == LIBXSVF_TAP_IRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IREXIT1;
+			}
+			break;
+		case LIBXSVF_TAP_IRSHIFT:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_IREXIT1;
+			break;
+		case LIBXSVF_TAP_IREXIT1:
+			if (s == LIBXSVF_TAP_IRPAUSE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRPAUSE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_IRPAUSE:
+			tap_transition(h, 1);
+			h->tap_state = LIBXSVF_TAP_IREXIT2;
+			break;
+		case LIBXSVF_TAP_IREXIT2:
+			if (s == LIBXSVF_TAP_IRSHIFT) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IRSHIFT;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_IRUPDATE;
+			}
+			break;
+		case LIBXSVF_TAP_IRUPDATE:
+			if (s == LIBXSVF_TAP_IDLE) {
+				tap_transition(h, 0);
+				h->tap_state = LIBXSVF_TAP_IDLE;
+			} else {
+				tap_transition(h, 1);
+				h->tap_state = LIBXSVF_TAP_DRSELECT;
+			}
+			break;
+
+		default:
+			LIBXSVF_HOST_REPORT_ERROR("Illegal tap state.");
+			return -1;
+		}
+		if (h->report_tapstate)
+			LIBXSVF_HOST_REPORT_TAPSTATE();
+		if (i>10) {
+			LIBXSVF_HOST_REPORT_ERROR("Loop in tap walker.");
+			return -1;
+		}
+	}
+
+	return 0;
+}
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/libxsvf/xsvf.c b/tools/nfipjtag/nfipjtag/libxsvf/xsvf.c
new file mode 100644
index 0000000000000000000000000000000000000000..f6203c5466d85127e50a6e9e67d528c524037b32
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/libxsvf/xsvf.c
@@ -0,0 +1,467 @@
+/*
+ *  Lib(X)SVF  -  A library for implementing SVF and XSVF JTAG players
+ *
+ *  Copyright (C) 2009  RIEGL Research ForschungsGmbH
+ *  Copyright (C) 2009  Clifford Wolf <clifford@clifford.at>
+ *  
+ *  Permission to use, copy, modify, and/or distribute this software for any
+ *  purpose with or without fee is hereby granted, provided that the above
+ *  copyright notice and this permission notice appear in all copies.
+ *  
+ *  THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ *  WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ *  MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ *  ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ *  WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ *  ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ *  OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ *
+ */
+
+#include "libxsvf.h"
+
+/* command codes as defined in xilinx xapp503 */
+enum xsvf_cmd {
+	XCOMPLETE       = 0x00,
+	XTDOMASK        = 0x01,
+	XSIR            = 0x02,
+	XSDR            = 0x03,
+	XRUNTEST        = 0x04,
+	XREPEAT         = 0x07,
+	XSDRSIZE        = 0x08,
+	XSDRTDO         = 0x09,
+	XSETSDRMASKS    = 0x0A,
+	XSDRINC         = 0x0B,
+	XSDRB           = 0x0C,
+	XSDRC           = 0x0D,
+	XSDRE           = 0x0E,
+	XSDRTDOB        = 0x0F,
+	XSDRTDOC        = 0x10,
+	XSDRTDOE        = 0x11,
+	XSTATE          = 0x12,
+	XENDIR          = 0x13,
+	XENDDR          = 0x14,
+	XSIR2           = 0x15,
+	XCOMMENT        = 0x16,
+	XWAIT           = 0x17
+};
+
+// This is to not confuse the VIM syntax highlighting
+#define VAL_OPEN (
+#define VAL_CLOSE )
+
+#define READ_BITS(_buf, _len) do {                                          \
+	unsigned char *_p = _buf; int _i;                                   \
+	for (_i=0; _i<(_len); _i+=8) {                                      \
+		int tmp = LIBXSVF_HOST_GETBYTE();                           \
+		if (tmp < 0) {                                              \
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");       \
+			goto error;                                         \
+		}                                                           \
+		*(_p++) = tmp;                                              \
+	}                                                                   \
+} while (0)
+
+#define READ_LONG() VAL_OPEN{                                               \
+	long _buf = 0; int _i;                                              \
+	for (_i=0; _i<4; _i++) {                                            \
+		int tmp = LIBXSVF_HOST_GETBYTE();                           \
+		if (tmp < 0) {                                              \
+			LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");       \
+			goto error;                                         \
+		}                                                           \
+		_buf = _buf << 8 | tmp;                                     \
+	}                                                                   \
+	_buf;                                                               \
+}VAL_CLOSE
+
+#define READ_BYTE() VAL_OPEN{                                               \
+	int _tmp = LIBXSVF_HOST_GETBYTE();                                  \
+	if (_tmp < 0) {                                                     \
+		LIBXSVF_HOST_REPORT_ERROR("Unexpected EOF.");               \
+		goto error;                                                 \
+	}                                                                   \
+	_tmp;                                                               \
+}VAL_CLOSE
+
+#define SHIFT_DATA(_inp, _outp, _maskp, _len, _state, _estate, _edelay, _ret) do { \
+	if (shift_data(h, _inp, _outp, _maskp, _len, _state, _estate, _edelay, _ret) < 0) { \
+		goto error;                                                 \
+	}                                                                   \
+} while (0)
+
+#define TAP(_state) do {                                                    \
+	if (libxsvf_tap_walk(h, _state) < 0)                                \
+		goto error;                                                 \
+} while (0)
+
+static int bits2bytes(int bits)
+{
+	return (bits+7) / 8;
+}
+
+static int getbit(unsigned char *data, int n)
+{
+	return (data[n/8] & (1 << (7 - n%8))) ? 1 : 0;
+}
+
+static void setbit(unsigned char *data, int n, int v)
+{
+	unsigned char mask = 1 << (7 - n%8);
+	if (v)
+		data[n/8] |= mask;
+	else
+		data[n/8] &= ~mask;
+}
+
+static int xilinx_tap(int state)
+{
+	/* state codes as defined in xilinx xapp503 */
+	switch (state)
+	{
+	case 0x00:
+		return LIBXSVF_TAP_RESET;
+		break;
+	case 0x01:
+		return LIBXSVF_TAP_IDLE;
+		break;
+	case 0x02:
+		return LIBXSVF_TAP_DRSELECT;
+		break;
+	case 0x03:
+		return LIBXSVF_TAP_DRCAPTURE;
+		break;
+	case 0x04:
+		return LIBXSVF_TAP_DRSHIFT;
+		break;
+	case 0x05:
+		return LIBXSVF_TAP_DREXIT1;
+		break;
+	case 0x06:
+		return LIBXSVF_TAP_DRPAUSE;
+		break;
+	case 0x07:
+		return LIBXSVF_TAP_DREXIT2;
+		break;
+	case 0x08:
+		return LIBXSVF_TAP_DRUPDATE;
+		break;
+	case 0x09:
+		return LIBXSVF_TAP_IRSELECT;
+		break;
+	case 0x0A:
+		return LIBXSVF_TAP_IRCAPTURE;
+		break;
+	case 0x0B:
+		return LIBXSVF_TAP_IRSHIFT;
+		break;
+	case 0x0C:
+		return LIBXSVF_TAP_IREXIT1;
+		break;
+	case 0x0D:
+		return LIBXSVF_TAP_IRPAUSE;
+		break;
+	case 0x0E:
+		return LIBXSVF_TAP_IREXIT2;
+		break;
+	case 0x0F:
+		return LIBXSVF_TAP_IRUPDATE;
+		break;
+	}
+	return -1;
+}
+
+static int shift_data(struct libxsvf_host *h, unsigned char *inp, unsigned char *outp, unsigned char *maskp, int len, enum libxsvf_tap_state state, enum libxsvf_tap_state estate, int edelay, int retries)
+{
+	int left_padding = (8 - len % 8) % 8;
+	int with_retries = retries > 0;
+	int i;
+
+	if (with_retries && LIBXSVF_HOST_SYNC() < 0) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		return -1;
+	}
+
+	while (1)
+	{
+		int tdo_error = 0;
+		int tms = 0;
+
+		TAP(state);
+		tms = 0;
+
+		for (i=len+left_padding-1; i>=left_padding; i--) {
+			if (i == left_padding && h->tap_state != estate) {
+				h->tap_state++;
+				tms = 1;
+			}
+			int tdi = getbit(inp, i);
+			int tdo = -1;
+			if (maskp && getbit(maskp, i))
+				tdo = outp && getbit(outp, i);
+			int sync = with_retries && i == left_padding;
+			if (LIBXSVF_HOST_PULSE_TCK(tms, tdi, tdo, 0, sync) < 0)
+				tdo_error = 1;
+		}
+
+		if (tms)
+			LIBXSVF_HOST_REPORT_TAPSTATE();
+	
+		if (edelay) {
+			TAP(LIBXSVF_TAP_IDLE);
+			LIBXSVF_HOST_UDELAY(edelay, 0, edelay);
+		} else {
+			TAP(estate);
+		}
+
+		if (!tdo_error)
+			return 0;
+
+		if (retries <= 0) {
+			LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+			return -1;
+		}
+
+		retries--;
+	}
+
+error:
+	return -1;
+}
+
+int libxsvf_xsvf(struct libxsvf_host *h)
+{
+	int rc = 0;
+	int i, j;
+
+	unsigned char *buf_tdi_data = (void*)0;
+	unsigned char *buf_tdo_data = (void*)0;
+	unsigned char *buf_tdo_mask = (void*)0;
+	unsigned char *buf_addr_mask = (void*)0;
+	unsigned char *buf_data_mask = (void*)0;
+
+	long state_dr_size = 0;
+	long state_data_size = 0;
+	long state_runtest = 0;
+	unsigned char state_xendir = 0;
+	unsigned char state_xenddr = 0;
+	unsigned char state_retries = 0;
+
+	while (1)
+	{
+		unsigned char cmd = LIBXSVF_HOST_GETBYTE();
+
+#define STATUS(_c) LIBXSVF_HOST_REPORT_STATUS("XSVF Command " #_c);
+
+		switch (cmd)
+		{
+		case XCOMPLETE: {
+			STATUS(XCOMPLETE);
+			goto got_complete_command;
+		  }
+		case XTDOMASK: {
+			STATUS(XTDOMASK);
+			READ_BITS(buf_tdo_mask, state_dr_size);
+			break;
+		  }
+		case XSIR: {
+			STATUS(XSIR);
+			int length = READ_BYTE();
+			unsigned char buf[bits2bytes(length)];
+			READ_BITS(buf, length);
+			SHIFT_DATA(buf, (void*)0, (void*)0, length, LIBXSVF_TAP_IRSHIFT,
+					state_xendir ? LIBXSVF_TAP_IRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XSDR: {
+			STATUS(XSDR);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xendir ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XRUNTEST: {
+			STATUS(XRUNTEST);
+			state_runtest = READ_LONG();
+			break;
+		  }
+		case XREPEAT: {
+			STATUS(XREPEAT);
+			state_retries = READ_BYTE();
+			break;
+		  }
+		case XSDRSIZE: {
+			STATUS(XSDRSIZE);
+			state_dr_size = READ_LONG();
+			buf_tdi_data = LIBXSVF_HOST_REALLOC(buf_tdi_data, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDI_DATA);
+			buf_tdo_data = LIBXSVF_HOST_REALLOC(buf_tdo_data, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDO_DATA);
+			buf_tdo_mask = LIBXSVF_HOST_REALLOC(buf_tdo_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_TDO_MASK);
+			buf_addr_mask = LIBXSVF_HOST_REALLOC(buf_addr_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_ADDR_MASK);
+			buf_data_mask = LIBXSVF_HOST_REALLOC(buf_data_mask, bits2bytes(state_dr_size), LIBXSVF_MEM_XSVF_DATA_MASK);
+			if (!buf_tdi_data || !buf_tdo_data || !buf_tdo_mask || !buf_addr_mask || !buf_data_mask) {
+				LIBXSVF_HOST_REPORT_ERROR("Allocating memory failed.");
+				goto error;
+			}
+			break;
+		  }
+		case XSDRTDO: {
+			STATUS(XSDRTDO);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xendir ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XSETSDRMASKS: {
+			STATUS(XSETSDRMASKS);
+			READ_BITS(buf_addr_mask, state_dr_size);
+			READ_BITS(buf_data_mask, state_dr_size);
+			state_data_size = 0;
+			for (i=0; i<state_dr_size; i++)
+				state_data_size += getbit(buf_data_mask, i);
+			break;
+		  }
+		case XSDRINC: {
+			STATUS(XSDRINC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			int num = READ_BYTE();
+			while (1) {
+				SHIFT_DATA(buf_tdi_data, buf_tdo_data, buf_tdo_mask, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+						state_xendir ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE,
+						state_runtest, state_retries);
+				if (num-- <= 0)
+					break;
+				int carry = 1;
+				for (i=state_dr_size-1; i>=0; i--) {
+					if (getbit(buf_addr_mask, i) == 0)
+						continue;
+					if (getbit(buf_tdi_data, i)) {
+						setbit(buf_tdi_data, i, !carry);
+					} else {
+						setbit(buf_tdi_data, i, carry);
+						carry = 0;
+					}
+				}
+				unsigned char this_byte = 0;
+				for (i=0, j=0; i<state_data_size; i++) {
+					if (i%8 == 0)
+						this_byte = READ_BYTE();
+					while (getbit(buf_data_mask, j) == 0)
+						j++;
+					setbit(buf_tdi_data, j++, getbit(&this_byte, i%8));
+				}
+			}
+			break;
+		  }
+		case XSDRB: {
+			STATUS(XSDRB);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRC: {
+			STATUS(XSDRC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRE: {
+			STATUS(XSDRE);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, (void*)0, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xendir ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE, 0, 0);
+			break;
+		  }
+		case XSDRTDOB: {
+			STATUS(XSDRTDOB);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRTDOC: {
+			STATUS(XSDRTDOC);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT, LIBXSVF_TAP_DRSHIFT, 0, 0);
+			break;
+		  }
+		case XSDRTDOE: {
+			STATUS(XSDRTDOE);
+			READ_BITS(buf_tdi_data, state_dr_size);
+			READ_BITS(buf_tdo_data, state_dr_size);
+			SHIFT_DATA(buf_tdi_data, buf_tdo_data, (void*)0, state_dr_size, LIBXSVF_TAP_DRSHIFT,
+					state_xendir ? LIBXSVF_TAP_DRPAUSE : LIBXSVF_TAP_IDLE, 0, 0);
+			break;
+		  }
+		case XSTATE: {
+			STATUS(XSTATE);
+			unsigned char state = READ_BYTE();
+			TAP(xilinx_tap(state));
+			break;
+		  }
+		case XENDIR: {
+			STATUS(XENDIR);
+			state_xendir = READ_BYTE();
+			break;
+		  }
+		case XENDDR: {
+			STATUS(XENDDR);
+			state_xenddr = READ_BYTE();
+			break;
+		  }
+		case XSIR2: {
+			STATUS(XSIR2);
+			int length = READ_BYTE();
+			length = length << 8 | READ_BYTE();
+			unsigned char buf[bits2bytes(length)];
+			READ_BITS(buf, length);
+			SHIFT_DATA(buf, (void*)0, (void*)0, length, LIBXSVF_TAP_IRSHIFT,
+					state_xendir ? LIBXSVF_TAP_IRPAUSE : LIBXSVF_TAP_IDLE,
+					state_runtest, state_retries);
+			break;
+		  }
+		case XCOMMENT: {
+			STATUS(XCOMMENT);
+			unsigned char this_byte;
+			do {
+				this_byte = READ_BYTE();
+			} while (this_byte);
+			break;
+		  }
+		case XWAIT: {
+			STATUS(XWAIT);
+			unsigned char state1 = READ_BYTE();
+			unsigned char state2 = READ_BYTE();
+			long usecs = READ_LONG();
+			TAP(xilinx_tap(state1));
+			LIBXSVF_HOST_UDELAY(usecs, 0, 0);
+			TAP(xilinx_tap(state2));
+			break;
+		  }
+		default:
+			LIBXSVF_HOST_REPORT_ERROR("Unknown XSVF command.");
+			goto error;
+		}
+	}
+
+error:
+	rc = -1;
+
+got_complete_command:
+	if (LIBXSVF_HOST_SYNC() != 0 && rc >= 0 ) {
+		LIBXSVF_HOST_REPORT_ERROR("TDO mismatch.");
+		rc = -1;
+	}
+
+	LIBXSVF_HOST_REALLOC(buf_tdi_data, 0, LIBXSVF_MEM_XSVF_TDI_DATA);
+	LIBXSVF_HOST_REALLOC(buf_tdo_data, 0, LIBXSVF_MEM_XSVF_TDO_DATA);
+	LIBXSVF_HOST_REALLOC(buf_tdo_mask, 0, LIBXSVF_MEM_XSVF_TDO_MASK);
+	LIBXSVF_HOST_REALLOC(buf_addr_mask, 0, LIBXSVF_MEM_XSVF_ADDR_MASK);
+	LIBXSVF_HOST_REALLOC(buf_data_mask, 0, LIBXSVF_MEM_XSVF_DATA_MASK);
+
+	return rc;
+}
diff --git a/tools/nfipjtag/nfipjtag/src/jtag.cpp b/tools/nfipjtag/nfipjtag/src/jtag.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d2fa46e9df98657cbb754f0fbcd0f568a92cd9cd
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/src/jtag.cpp
@@ -0,0 +1,561 @@
+/*!
+ * @file   jtag.c
+ * @brief  WorldFIP JTAG programmer
+ * @author Stephen Page
+ * @author Michael Davis
+ *
+ * The functions within this file read an SVF or XSVF input file and translate the contents into JTAG signals (TDI, TMS and TDO)
+ * using libxsvf. TDI and TMS are buffered into structures, which are then added to a message queue for transmission over a
+ * WorldFIP bus. A second message queue is used to receive TDO for verification against expected values.
+ */
+
+#include <stdio.h>
+#include <errno.h>
+#include <unistd.h>
+#include <sys/stat.h>
+#include <time.h>
+#include <stdlib.h>
+#include <libxsvf.h>
+#include <jtag.h>
+#include <nfipjtag_cycle.h>
+
+// Constants
+
+#define MAX_JTAG_BITS             (4 * 122)       // 4 pairs of [TMS,TDI] per transmitted byte
+
+// Global variables
+
+struct Jtag
+{
+    bool                          error;          // Flag to indicate that an error has occurred
+
+    int                           input_fd;       // File descriptor for the input file
+    struct stat                   input_stat;     // File status/size for the input file
+    int32_t                       bits_sent;      // For progress meter
+    int32_t                       bytes_read;     // For progress meter
+    int32_t                       file_size;      // For progress meter
+    int32_t                       read_rate;      // Bytes read from file per second
+    int32_t                       sent_rate;      // JTAG TDI bits sent per second
+    bool                          eof;            // Flag to indicate that the end of the input file has been reached
+
+    struct Nfipjtag_tx_queue_item tx_msg;         // item to add to the transmit queue
+};
+
+struct Jtag jtag = {};
+
+// Static variables
+
+static time_t   start_time;                             // Start time for playing the current file
+static uint32_t jtag_bits_histogram[MAX_JTAG_BITS+1];   // Histogram of JTAG TDI bits sent
+
+// Static functions
+
+/*
+ * Calculate playing rate in bytes/sec since start of file
+ */
+
+uint32_t jtagCalculateRates(time_t time_now)
+{
+    const uint32_t elapsed_time = (uint32_t)(time_now - start_time);
+
+    if(elapsed_time > 0)
+    {
+        jtag.read_rate = jtag.bytes_read / elapsed_time;
+        jtag.sent_rate = jtag.bits_sent  / elapsed_time;
+    }
+
+    return elapsed_time;
+}
+
+
+
+/*
+ * Add a JTAG bit to the transmission buffer
+ */
+
+inline void jtagSetBit(int jtag_bit_value)
+{
+    // Calculate the byte and bit indexes
+
+    uint32_t byte_bit_index = jtag.tx_msg.payload.length_bits % 8;
+    uint32_t byte_index     = jtag.tx_msg.payload.length_bits / 8;
+
+    // Clear the byte if necessary
+
+    if(!byte_bit_index)
+    {
+        jtag.tx_msg.payload.jtag_bits[byte_index] = 0;
+    }
+
+    // Set the bit within the transmission buffer
+
+    if(jtag_bit_value)
+    {
+        jtag.tx_msg.payload.jtag_bits[byte_index] |= 0x80 >> byte_bit_index;
+    }
+
+    jtag.tx_msg.payload.length_bits++;
+}
+
+
+
+/*
+ * Set JTAG signals
+ */
+
+static void jtagSet(int tdi, int tms)
+{
+    // Do nothing if an error has occurred
+
+    if(jtag.error)
+    {
+        return;
+    }
+
+    // Add JTAG bits to the transmission buffer
+
+    jtagSetBit(tms);
+    jtagSetBit(tdi);
+
+    // If the end of the input file has been reached, or a response is requexted, 
+    // or the buffer is full then add the message to the transmission queue 
+
+    if(jtag.eof || jtag.tx_msg.rx_request ||
+       jtag.tx_msg.payload.length_bits >= sizeof(jtag.tx_msg.payload.jtag_bits) * 8)
+    {
+        jtag.tx_msg.delay_us = 0;
+
+        // Add the message to the transmission queue
+
+        if(mq_send(nfipjtag_q.tx_queue, reinterpret_cast<char*>(&jtag.tx_msg), sizeof(jtag.tx_msg), 0))
+        {
+            perror("jtagSet(): mq_send");
+            jtag.error = true;
+        }
+
+        // Record the number of TDI bits sent and reset the transmit length
+
+        const uint32_t bits_sent = jtag.tx_msg.payload.length_bits / 2;   // TMS and TDI are sent for each clock
+
+        jtag.bits_sent += bits_sent;
+        jtag_bits_histogram[bits_sent]++;
+        jtag.tx_msg.payload.length_bits = 0;
+    }
+}
+
+
+
+/*
+ * Get JTAG TDO signal
+ */
+
+static int jtagGetTdo(void)
+{
+    // Do nothing if an error has occurred
+
+    if(jtag.error)
+    {
+        return -1;
+    }
+
+    // Wait to receive JTAG status
+
+    ssize_t                size;
+    struct Nfipjtag_rx_var rx_var;
+    struct timespec        timeout;
+
+    timeout.tv_sec  = 1;
+    timeout.tv_nsec = 0;
+
+    while(!jtag.error &&
+          (size = mq_timedreceive(nfipjtag_q.rx_queue, reinterpret_cast<char*>(&rx_var), sizeof(rx_var), NULL, &timeout)) == -1 &&
+          errno == ETIMEDOUT);
+
+    // Check whether an error occurred
+
+    if(jtag.error)
+    {
+        return -1;
+    }
+
+    if(size != sizeof(rx_var))
+    {
+        if(errno)
+        {
+            perror("jtagGetTdo(): mq_timedreceive");
+        }
+
+        return -1;
+    }
+
+    // Return TDO
+
+    return rx_var.tdo;
+}
+
+
+
+/*
+ * Callback for libxsvf to set-up the JTAG interface
+ */
+
+static int jtagXsvfSetup(struct libxsvf_host *h)
+{
+    return 0;
+}
+
+
+
+/*
+ * Callback for libxsvf to shutdown the JTAG interface
+ */
+
+static int jtagXsvfShutdown(struct libxsvf_host *h)
+{
+    return 0;
+}
+
+
+
+/*
+ * Callback for libxsvf to delay
+ */
+
+static void jtagXsvfUDelay(struct libxsvf_host *h, long usecs, int tms, long num_tck)
+{
+    // Do nothing if an error has occurred
+
+    if(jtag.error)
+    {
+        return;
+    }
+
+    // Request a delay
+    if(usecs)
+    {
+        // Request a delay and add the message to the transmission queue
+
+        jtag.tx_msg.delay_us   = usecs;
+        jtag.tx_msg.rx_request = 0;
+
+        if(mq_send(nfipjtag_q.tx_queue, reinterpret_cast<char*>(&jtag.tx_msg), sizeof(jtag.tx_msg), 0))
+        {
+            perror("jtagXsvfUDelay(): mq_send");
+            jtag.error = true;
+            return;
+        }
+
+        // Record the number of TDI bits sent and reset the transmit length
+        uint32_t bits_sent = jtag.tx_msg.payload.length_bits / 2;   // TMS and TDI are sent for each clock
+        jtag.bits_sent += bits_sent;
+        jtag_bits_histogram[bits_sent]++;
+
+        // Reset the length
+        jtag.tx_msg.payload.length_bits = 0;
+    }
+
+    // Toggle clock if requested
+
+    while(num_tck--)
+    {
+        jtagSet(0, tms);
+    }
+}
+
+
+
+/*
+ * Callback for libxsvf to read a byte from the input file
+ */
+
+static int jtagXsvfGetByte(struct libxsvf_host *h)
+{
+    uint8_t c;
+    ssize_t size;
+
+    // Do nothing if an error has occurred
+
+    if(jtag.error)
+    {
+        return -1;
+    }
+
+    // Read next byte from the input file
+
+    if((size = read(jtag.input_fd, &c, 1)) == -1)
+    {
+        perror("jtagXsvfGetByte(): read");
+        jtag.error = true;
+        return -1;
+    }
+
+    jtag.bytes_read++;
+
+    // Print progress at the start, every 64 kilobytes and at end-of-file
+
+    if(quiet_mode == false && (jtag.bytes_read % 65536 == 0 || size == 0))
+    {
+        jtagCalculateRates(time(NULL));
+
+        printf(" %3d%% %u KB @ %u KB/s %u b/s\r",
+               (int)(((float)jtag.bytes_read / jtag.input_stat.st_size) * 100),
+               jtag.bytes_read / 1024,
+               jtag.read_rate / 1024,
+               jtag.sent_rate);
+        fflush(stdout);
+    }
+
+    // Handle end-of-file
+
+    if(size == 0)
+    {
+        // Set a flag to ensure that all data is queued
+
+        jtag.eof = true;
+        return -1;
+    }
+
+    return c;
+}
+
+
+
+/*
+ * Callback for libxsvf to send JTAG signals
+ */
+
+static int jtagXsvfPulseTck(struct libxsvf_host *h, int tms, int tdi, int tdo, int rmask, int sync)
+{
+    int response = 1;
+    static int fast_mode_check_cnt = 0;
+
+    // Request reception of TDO when required
+    if(tdo != -1)
+    {
+        fast_mode_check_cnt++;
+
+        // In fast mode, check tdo once every 63 cycles, in other cycles assume a valid tdo has been received
+        if(!fast_mode || (fast_mode_check_cnt % 63U == 0))
+        {
+            jtag.tx_msg.rx_request = 1U;
+        }
+        else
+        {
+            jtag.tx_msg.rx_request = 0U;
+            response = tdo;
+        }
+    }
+    else
+    {
+        jtag.tx_msg.rx_request = 0U;
+    }
+
+    // Prepare to transmit TDI and TMS
+
+    jtagSet(tdi == -1 ? 0 : tdi, tms);
+
+    // Return 1 if TDO comparison is not requested
+
+    if(jtag.tx_msg.rx_request)
+    {
+        // TDO response has been requested so wait for it
+
+        const int tdo_value = jtagGetTdo();
+
+        // Return TDO if it matches expected value, otherwise return -1
+
+        response = (!jtag.error && tdo_value == tdo) ? tdo_value : -1;
+    }
+
+    return response;
+}
+
+
+
+/*
+ * Callback for libxsvf to set TCK frequency
+ */
+
+static int jtagXsvfSetFrequency(struct libxsvf_host *h, int v)
+{
+    // Ignore frequency request
+
+    return 0;
+}
+
+
+
+/*
+ * Callback for libxsvf to report an error
+ */
+
+static void jtagXsvfReportError(struct libxsvf_host *h, const char *file, int line, const char *message)
+{
+    // Report the error if one has not already been reported elsewhere
+
+    if(!jtag.error)
+    {
+        printf("\n");
+        fprintf(stderr, "Error: %s\n", message);
+    }
+}
+
+
+
+/*
+ * Callback for libxsvf to re-allocate memory
+ */
+
+static void *jtagXsvfRealloc(struct libxsvf_host *h, void *ptr, int size, enum libxsvf_mem which)
+{
+    return realloc(ptr, size);
+}
+
+
+
+// External functions
+
+int jtagProg(char *filename, enum jtag_file_type type)
+{
+    struct libxsvf_host xsvf = {};
+
+    // Set libxsvf callbacks
+
+    xsvf.getbyte       = jtagXsvfGetByte;
+    xsvf.pulse_tck     = jtagXsvfPulseTck;
+    xsvf.realloc       = jtagXsvfRealloc;
+    xsvf.report_error  = jtagXsvfReportError;
+    xsvf.set_frequency = jtagXsvfSetFrequency;
+    xsvf.setup         = jtagXsvfSetup;
+    xsvf.shutdown      = jtagXsvfShutdown;
+    xsvf.udelay        = jtagXsvfUDelay;
+
+    // Set file mode
+
+    enum libxsvf_mode  xsvf_file_mode = type == JTAG_FILE_XSVF ? LIBXSVF_MODE_XSVF : LIBXSVF_MODE_SVF;
+
+    // Open input file
+
+    jtag.bytes_read = 0;
+    jtag.eof        = false;
+
+    if((jtag.input_fd = open(filename, O_RDONLY)) == -1)
+    {
+        perror("jtagProg(): Open input file");
+        return -1;
+    }
+
+    // Get input file status
+
+    if(fstat(jtag.input_fd, &jtag.input_stat))
+    {
+        perror("jtagProg(): fstat");
+        close(jtag.input_fd);
+        return -1;
+    }
+
+    jtag.file_size = jtag.input_stat.st_size;
+    
+    // Print start time
+
+    start_time = time(NULL);
+
+    printf("JTAG operation started at   %s", ctime(&start_time));
+
+    // Perform JTAG programming
+
+    if(libxsvf_play(&xsvf, xsvf_file_mode) < 0 || jtag.error)
+    {
+        const time_t   end_time     = time(NULL);
+        const uint32_t elapsed_time = jtagCalculateRates(end_time);
+
+        printf("\rJTAG operation aborted at   %sElapsed time: %u s.  At byte %u.\n",
+                   ctime(&end_time),
+                   elapsed_time,
+                   jtag.bytes_read);
+
+        if(debug_mode)
+        {
+            ssize_t size;
+            char next_file_data[1025];
+
+            size = read(jtag.input_fd, &next_file_data, sizeof(next_file_data)-1);
+
+            if(size > 0)
+            {
+                next_file_data[size] = '\0';
+                printf("Continuing in file:\n%s...\n", next_file_data);
+            }
+        }
+
+        close(jtag.input_fd);
+        return -1;
+    }
+
+    close(jtag.input_fd);
+
+    // Wait for all transmission queue entries to be processed
+
+    struct mq_attr mq_attr;
+
+    do
+    {
+        // Get transmission queue attributes
+
+        if(mq_getattr(nfipjtag_q.tx_queue, &mq_attr))
+        {
+            perror("jtagProg(): mq_get_attr");
+            return -1;
+        }
+
+        // Sleep for 100 ms to allow transmission
+
+        struct timespec sleep_time;
+
+        sleep_time.tv_sec  = 0;
+        sleep_time.tv_nsec = 100000000;
+
+        while(nanosleep(&sleep_time, &sleep_time) && errno == EINTR);
+
+    } while(mq_attr.mq_curmsgs); // Transmission queue is not empty
+
+    // Print end time, elapsed time and rate
+
+    const time_t   end_time     = time(NULL);
+    const uint32_t elapsed_time = jtagCalculateRates(end_time);
+
+    printf("\rJTAG operation completed at %sElapsed time: %u s.  Read %u KB @ %u KB/s.  Sent %u b @ %u b/s.\n",
+               ctime(&end_time),
+               elapsed_time,
+               jtag.bytes_read / 1024,
+               jtag.read_rate / 1024,
+               jtag.bits_sent,
+               jtag.sent_rate);
+
+    // Dump histogram of number of jtag bits sent per transmission
+
+    uint32_t i;
+
+    if(quiet_mode == false)
+    {
+        for(i = 0 ; i <= MAX_JTAG_BITS; i++)
+        {
+            if(jtag_bits_histogram[i] > 0)
+            {
+                printf("%03u ; %u\n", i, jtag_bits_histogram[i]);
+            }
+        }
+    }
+
+    return 0;
+}
+
+
+
+void jtagSignalHandler(int signal)
+{
+    fprintf(stderr, "Received signal %d\n", signal);
+    jtag.error = true;
+}
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/src/nfipjtag_cycle.mfip.cpp b/tools/nfipjtag/nfipjtag/src/nfipjtag_cycle.mfip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c99ddf9496c5c1c54b1ce2f644836bfb208b9684
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/src/nfipjtag_cycle.mfip.cpp
@@ -0,0 +1,568 @@
+/*!
+ * @file   nfipjtag_cycle.cpp
+ * @brief  Define the BA program to reprogram FPGAs attached to a nanoFIP
+ * @author Michael Davis
+ */
+
+#include <cstdio>
+#include <cerrno>
+#include <unistd.h>
+#include <signal.h>
+#include <sys/stat.h>
+#include <netinet/in.h>
+#include <time.h>
+#include <string.h>
+
+#include <fip.mfip.h>
+#include <nfipjtag_cycle.h>
+
+// Constants and enumerated types
+
+// POSIX queues
+
+const uint32_t JTAG_RX_QUEUE_LENGTH   = 1;           // Reception message queue length
+const char*    JTAG_RX_QUEUE_NAME     = "/jtag_rx";  // Name of reception message queue
+const uint32_t JTAG_TX_QUEUE_LENGTH   = 10;          // Transmission message queue length
+const char*    JTAG_TX_QUEUE_NAME     = "/jtag_tx";  // Name of transmission message queue
+
+// Nanofip status mask
+
+enum Nfipjtag_nanofip_status_mask
+{
+    JTAG_NFIP_STAT_U_CACER = 0x04,                   // User consumed variable access error
+    JTAG_NFIP_STAT_U_PACER = 0x08,                   // User produced variable access error
+    JTAG_NFIP_STAT_R_TLER  = 0x10,                   // Received control or PDU_TYPE error or incoherent length
+    JTAG_NFIP_STAT_R_FCSER = 0x20,                   // Received FCS or Manchester encoding or bit number error
+    JTAG_NFIP_STAT_T_TXER  = 0x40,                   // Transmit error (FIELDRIVE)
+    JTAG_NFIP_STAT_T_WDER  = 0x80                    // Watchdog error (FIELDRIVE)
+};
+
+const uint8_t JTAG_NFIP_STAT_BITS = JTAG_NFIP_STAT_R_TLER | JTAG_NFIP_STAT_R_FCSER | JTAG_NFIP_STAT_T_TXER  | JTAG_NFIP_STAT_T_WDER;
+
+/*!
+ * Unique frame IDs for the BA program, as defined in the Nfipjtag Fieldbus Cycle document, section 3.
+ * These values specify ID_DAT for each frame. Each value must be used at most once per cycle. Note
+ * that the packing ID is reserved (0x9080).
+ */
+
+enum Nfipjtag_protocol_frame_id
+{
+    NFIPJTAG_NOID,                            //!< Used for frames which do not correspond to a variable
+
+    NFIPJTAG_FIP_TRST            = 0x0500,    //!< nanoFIP VAR1 frame to set TRST high or low. Last two bytes specify the FIP address.
+    NFIPJTAG_FIP_ID_TX           = 0xAA00,    //!< JTAG transmission frame. Last two bytes specify the FIP address.
+    NFIPJTAG_FIP_ID_RX           = 0xAB00,    //!< JTAG reception frame. Last two bytes specify the FIP address.
+
+    NFIPJTAG_NANOFIP_RESET       = 0xE000     //!< nanoFIP RESET (R) command. The addresses to reset is specified in the (two-byte) payload.
+};
+
+/*!
+ * Structure for Nfipjtag data pointers and device statuses
+ */
+
+struct Nfipjtag_data
+{
+    // WorldFIP cycle period (used to handle delay requests)
+
+    uint32_t cycle_period;
+
+    // Address of the device to be reprogrammed
+
+    uint8_t address;
+
+    // Flag to indicate device is still sending data
+
+    bool rx_status;
+
+    // Send response next cycle?
+
+    bool rx_request;
+
+    // Pointers to Nfipjtag variable buffers
+
+    struct Nfipjtag_TRST_command    *trst;                    //!< Command to set TRST high or low
+    struct Nfipjtag_tx_var          *tx_var;                  //!< Command to transmit JTAG data
+    struct Nfipjtag_rx_var          *rx_var;                  //!< nanoFIP response
+    struct Nfipjtag_nanofip_reset   *nanofip_reset;           //!< nanoFIP reset variable
+};
+
+// Declarations of callback functions
+
+static void nfipjtagTxVarSent(struct mstrfip_dev *dev,
+			struct mstrfip_data *data, struct mstrfip_irq *irq);
+
+static void nfipjtagRxVarReceived(struct mstrfip_dev *dev,
+			struct mstrfip_data *data, struct mstrfip_irq *irq);
+
+// Global variables
+
+extern bool     quiet_mode;                                 // From nfipjtag_main.cpp
+extern bool     debug_mode;                                 // From nfipjtag_main.cpp
+extern uint32_t ba_wait_time[2];                            // From nfipjtag_main.cpp
+extern uint32_t silence_time_us;                            // From nfipjtag_main.cpp
+
+/*
+ * Queue descriptors (extern). Initialised to zero.
+ */
+
+struct Nfipjtag_queues nfipjtag_q = {};
+
+/*
+ * Variable data pointers and IPC for Nfipjtag cycle. Initialised to zero.
+ */
+
+struct Nfipjtag_data nfipjtag = {};
+
+/*
+ * Address reinterpret cast macro (to make the FIP_protocol_frame tables below more legible)
+ */
+
+#define ADDR(x) reinterpret_cast<void **>(&x)
+
+/*
+ * Definition of the fieldbus cycle to set/unset TRST
+ *
+ * Each line defines one frame of the Bus Arbitrator macrocycle.
+ */
+
+struct jtag_fip_per_var_desc nfipjtag_per_varlist1[] = {
+    // send TRST
+    {NFIPJTAG_FIP_TRST, JTAG_FIP_PER_VAR_PROD, sizeof(Nfipjtag_TRST_command), NULL, ADDR(nfipjtag.trst)},
+};
+#define NFIPJTAG_PER_VARLIST1_NVAR sizeof(nfipjtag_per_varlist1) / sizeof(struct jtag_fip_per_var_desc)
+// Macro cycle duration for TRST cycle.
+// This time should be enough even for the lowest bus speed 31.25 Kb/s
+#define NFIPJTAG_TRST_MCYCLE_USTIME 10000
+
+/*!
+ * Definition of the fieldbus cycle to reprogram the FPGAs.
+ *
+ * Each line defines one frame of the Bus Arbitrator macrocycle.
+ */
+
+struct jtag_fip_per_var_desc nfipjtag_per_varlist2[] = {
+    // send JTAG TDI command
+    {NFIPJTAG_FIP_ID_TX, JTAG_FIP_PER_VAR_PROD, sizeof(Nfipjtag_tx_var), &nfipjtagTxVarSent, ADDR(nfipjtag.tx_var)},
+};
+#define NFIPJTAG_PER_VARLIST2_NVAR sizeof(nfipjtag_per_varlist2) / sizeof(struct jtag_fip_per_var_desc)
+
+struct jtag_fip_per_var_desc nfipjtag_per_varlist3[] = {
+    // Receive JTAG TDO reply
+    {NFIPJTAG_FIP_ID_RX, JTAG_FIP_PER_VAR_CONS, sizeof(Nfipjtag_rx_var), &nfipjtagRxVarReceived, ADDR(nfipjtag.rx_var)},
+};
+#define NFIPJTAG_PER_VARLIST3_NVAR sizeof(nfipjtag_per_varlist3) / sizeof(struct jtag_fip_per_var_desc)
+
+struct jtag_fip_mcycle_desc *nfipjtag_mcycle_desc = NULL;
+
+// Static functions
+
+#if 0
+/*
+ * Unpack and display contents of a transmit queue item.
+ *
+ * Useful for debugging: the bit patterns can be compared to what is seen on the nanoFIP TDI and TMS pins with a scope.
+ */
+
+static void debugPrintf(const struct Nfipjtag_tx_queue_item &item)
+{
+    uint8_t l;
+    uint8_t byte;
+    uint8_t bit;
+
+    fprintf(stderr, "delay_us = %d, rx_request = %d, length_bits = %d, jtag_bits = ", item.delay_us, item.rx_request, item.payload.length_bits);
+    for(l = 0; l <= item.payload.length_bits / 8; ++l)
+    {
+        fprintf(stderr, "%02x", item.payload.jtag_bits[l]);
+    }
+    fprintf(stderr, "\nTDI: ");
+    for(l = 0; l < item.payload.length_bits; l += 2)
+    {
+        byte = l / 8;
+        bit  = 0x40 >> (l % 8);
+        fprintf(stderr, "%d", (item.payload.jtag_bits[byte] & bit) ? 1 : 0);
+    }
+    fprintf(stderr, "\nTMS: ");
+    for(l = 0; l < item.payload.length_bits; l += 2)
+    {
+        byte = l / 8;
+        bit  = 0x80 >> (l % 8);
+        fprintf(stderr, "%d", (item.payload.jtag_bits[byte] & bit) ? 1 : 0);
+    }
+    fprintf(stderr, "\n");
+}
+#endif
+
+/*
+ * Callback after sending transmission variable
+ */
+
+static void nfipjtagTxVarSent(struct mstrfip_dev *dev,
+			struct mstrfip_data *data, struct mstrfip_irq *irq)
+{
+    static uint32_t               delay_cycles = 0;
+
+    mq_attr                       tx_q_attr;
+    struct Nfipjtag_tx_queue_item next_item;
+
+    // Check device is alive
+
+    if(!nfipjtag.rx_status)
+    {
+        fprintf(stderr, "\rFIP variable not received from device %d.\n", nfipjtag.address);
+        raise(SIGHUP);
+    }
+    nfipjtag.rx_status = false;
+
+    // Read a message from the transmit queue, if there is no delay this cycle and there is at least one item in the queue
+    // (as we don't want to block in a callback)
+
+    if(delay_cycles == 0 &&
+       mq_getattr(nfipjtag_q.tx_queue, &tx_q_attr) == 0 &&
+       tx_q_attr.mq_curmsgs > 0)
+    {
+        // Read data from the transmit queue
+
+        ssize_t qitem_size = mq_receive(nfipjtag_q.tx_queue, reinterpret_cast<char*>(&next_item), sizeof(struct Nfipjtag_tx_queue_item), NULL);
+
+        if(qitem_size == sizeof(struct Nfipjtag_tx_queue_item))
+        {
+            // Set delay_cycles if a delay was requested
+
+            if(next_item.delay_us > 0)
+            {
+                delay_cycles = next_item.delay_us / nfipjtag.cycle_period;
+                if(next_item.delay_us > 1000000 && quiet_mode == false)
+                {
+                    printf("  Delay for %ds                                   \r", next_item.delay_us / 1000000);
+                    fflush(stdout);
+                }
+            }
+
+            // The length is specified as big-endian (see EDMS document 1107940: nanoFIP Functional Specification, Annex: JTAG Feature)
+
+            next_item.payload.length_bits = htons(next_item.payload.length_bits);
+
+            nfipjtag.rx_request = next_item.rx_request;
+
+            memcpy(nfipjtag.tx_var, &next_item.payload, sizeof(struct Nfipjtag_tx_var));
+        }
+        else
+        {
+            perror("nfipjtagTxVarSent(): mq_receive");
+            raise(SIGHUP);
+        }
+
+    }
+    else if(delay_cycles > 0)
+    {
+        --delay_cycles;
+    }
+
+    // Write transmission variable
+
+    fipMfipWriteVar(NFIPJTAG_FIP_ID_TX | nfipjtag.address);
+
+    // Zero the length to stop data being sent again in next BA cycle
+
+    nfipjtag.tx_var->length_bits = 0;
+}
+
+/*
+ * Callback on arrival of reception variable
+ */
+
+static void nfipjtagRxVarReceived(struct mstrfip_dev *dev,
+			struct mstrfip_data *data, struct mstrfip_irq *irq)
+{
+    static FIP_device_stats error_stats = {};
+    static bool             rx_request  = false;
+
+    // Set device alive flag
+
+    nfipjtag.rx_status = true;
+
+    // Process the received status variable
+
+    if(!fipMfipReadVar(data, &error_stats))
+    {
+        fprintf(stderr, "WorldFIP error: MPS status fault.\n");
+        raise(SIGHUP);
+    }
+
+    // Check whether the nanoFIP reports an error
+
+    if(nfipjtag.rx_var->nfip_status & JTAG_NFIP_STAT_BITS)
+    {
+        printf("\n");
+        fprintf(stderr, "WorldFIP error: nanoFIP status 0x%02X\n", nfipjtag.rx_var->nfip_status);
+        raise(SIGHUP);
+    }
+
+    // Handle request for received data
+
+    if(rx_request)
+    {
+        mq_attr rx_q_attr;
+
+        // Check queue is not full (as we don't want to block in a callback)
+
+        if(mq_getattr(nfipjtag_q.tx_queue, &rx_q_attr) != 0)
+        {
+            perror("nfipjtagRxVarReceived(): mq_getattr");
+            raise(SIGHUP);
+        }
+
+        if(rx_q_attr.mq_curmsgs == rx_q_attr.mq_maxmsg)
+        {
+            fprintf(stderr, "nfipjtagRxVarReceived(): Receive queue is full, increase JTAG_RX_QUEUE_LENGTH and recompile.\n");
+            raise(SIGHUP);
+        }
+
+        // Add data to reception queue
+
+        if(mq_send(nfipjtag_q.rx_queue, reinterpret_cast<char*>(nfipjtag.rx_var), sizeof(struct Nfipjtag_rx_var), 0))
+        {
+            perror("nfipjtagRxVarReceived(): mq_send");
+            raise(SIGHUP);
+        }
+    }
+
+    rx_request          = nfipjtag.rx_request;
+    nfipjtag.rx_request = false;
+}
+
+void nfipjtagFreeMacroCycleDesc() {
+    if (nfipjtag_mcycle_desc == NULL)
+    	return;
+
+    delete nfipjtag_mcycle_desc;
+    nfipjtag_mcycle_desc = NULL;
+}
+
+#define NFIPJTAG_TRST_MCYCLE_DESC_NENTRIES 2
+static void nfipjtagTRSTBuildMacroCycleDesc()
+{
+    nfipjtagFreeMacroCycleDesc();
+
+    nfipjtag_mcycle_desc = new jtag_fip_mcycle_desc[NFIPJTAG_TRST_MCYCLE_DESC_NENTRIES];
+    /* periodic window */
+    nfipjtag_mcycle_desc[0].type = JTAG_FIP_PER_VAR_WIND;
+    nfipjtag_mcycle_desc[0].per_var_wind.nvar = NFIPJTAG_PER_VARLIST1_NVAR;
+    nfipjtag_mcycle_desc[0].per_var_wind.varlist = nfipjtag_per_varlist1;
+
+    /* wait window */
+    nfipjtag_mcycle_desc[1].type = JTAG_FIP_WAIT_WIND;
+    nfipjtag_mcycle_desc[1].wait_wind.end_ustime = NFIPJTAG_TRST_MCYCLE_USTIME;
+    nfipjtag_mcycle_desc[1].wait_wind.silent = 1;
+};
+
+// External functions
+
+int32_t nfipjtagCycleSetTRST(uint8_t address, enum Nfipjtag_TRST_value value)
+{
+    struct timespec sleep_time;
+
+    // Initialise the FIP BA macrocycle program
+
+    nfipjtag_per_varlist1[0].id &= 0xFF00;
+    nfipjtag_per_varlist1[0].id |= address;
+
+    nfipjtagTRSTBuildMacroCycleDesc();
+    fipMfipInitProtocol(nfipjtag_mcycle_desc, NFIPJTAG_TRST_MCYCLE_DESC_NENTRIES);
+
+    // Initialise FIP variables
+
+    nfipjtag.trst->low_high = value;
+
+    // Start the FIP BA macrocycle program
+
+    if(fipMfipStartProtocol() != 0)
+    {
+        fprintf(stderr, "nfipjtagCycleSetTRST(): Failed to start BA macrocycle with error %d.\n", errno);
+        nfipjtagFreeMacroCycleDesc();
+	return -1;
+    }
+
+    // Note: it is not possible to write variables before AE/LE has started
+
+    fipMfipWriteVars();
+
+    // wait a few cycles to process the command
+
+    sleep_time.tv_sec = 0;
+    sleep_time.tv_nsec = 100000000;
+    while(nanosleep(&sleep_time, &sleep_time) && errno == EINTR);
+
+    // Shut down the FIP BA macrocycle program
+
+    fipMfipStopProtocol();
+    return 0;
+}
+
+
+
+int32_t nfipjtagCycleInit(void)
+{
+    // Initialise the queues to communicate between the JTAG file parser and the Nfipjtag cycle thread.
+
+    struct mq_attr queue_attr = {};
+
+    // Message queues persist after the process quits, so remove and recreate them to be sure they are empty.
+
+    if((mq_unlink(JTAG_RX_QUEUE_NAME) == -1 && errno != ENOENT) ||
+       (mq_unlink(JTAG_TX_QUEUE_NAME) == -1 && errno != ENOENT))
+    {
+        fprintf(stderr, "nfipjtagCycleInit(): error unlinking message queues.\n");
+        return -1;
+    }
+
+    // Create reception message queue
+
+    queue_attr.mq_maxmsg  = JTAG_RX_QUEUE_LENGTH;
+    queue_attr.mq_msgsize = sizeof(struct Nfipjtag_rx_var);
+
+    nfipjtag_q.rx_queue = mq_open(JTAG_RX_QUEUE_NAME, O_RDWR | O_CREAT | O_EXCL, S_IREAD | S_IWRITE, &queue_attr);
+    if(nfipjtag_q.rx_queue == -1)
+    {
+        fprintf(stderr, "nfipjtagCycleInit(): error initialising reception message queue.\n");
+        return -1;
+    }
+
+    // Create transmission message queue
+
+    queue_attr.mq_maxmsg  = JTAG_TX_QUEUE_LENGTH;
+    queue_attr.mq_msgsize = sizeof(struct Nfipjtag_tx_queue_item);
+
+    nfipjtag_q.tx_queue = mq_open(JTAG_TX_QUEUE_NAME, O_RDWR | O_CREAT | O_EXCL, S_IREAD | S_IWRITE, &queue_attr);
+    if(nfipjtag_q.tx_queue == -1)
+    {
+        fprintf(stderr, "nfipjtagCycleInit(): error initialising transmission message queue.\n");
+        return -1;
+    }
+
+    return 0;
+}
+
+
+
+void nfipjtagCycleCleanUp(void)
+{
+    // Shut down FIP FDM if it is running
+
+    fipMfipStopInterface();
+
+    nfipjtagFreeMacroCycleDesc();
+
+    // Clean up the queues
+
+    mq_close(nfipjtag_q.rx_queue);
+    mq_close(nfipjtag_q.tx_queue);
+
+    mq_unlink(JTAG_RX_QUEUE_NAME);
+    mq_unlink(JTAG_TX_QUEUE_NAME);
+}
+
+
+#define NFIPJTAG_MCYCLE_DESC_NENTRIES 4
+static void nfipjtagBuildMacroCycleDesc(uint32_t *wait_time)
+{
+    nfipjtagFreeMacroCycleDesc();
+
+    nfipjtag_mcycle_desc = new jtag_fip_mcycle_desc[NFIPJTAG_MCYCLE_DESC_NENTRIES];
+    /* periodic window */
+    nfipjtag_mcycle_desc[0].type = JTAG_FIP_PER_VAR_WIND;
+    nfipjtag_mcycle_desc[0].per_var_wind.nvar = NFIPJTAG_PER_VARLIST2_NVAR;
+    nfipjtag_mcycle_desc[0].per_var_wind.varlist = nfipjtag_per_varlist2;
+
+    /* wait window */
+    nfipjtag_mcycle_desc[1].type = JTAG_FIP_WAIT_WIND;
+    nfipjtag_mcycle_desc[1].wait_wind.end_ustime = wait_time[0];
+    nfipjtag_mcycle_desc[1].wait_wind.silent = 1;
+
+    /* periodic window */
+    nfipjtag_mcycle_desc[2].type = JTAG_FIP_PER_VAR_WIND;
+    nfipjtag_mcycle_desc[2].per_var_wind.nvar = NFIPJTAG_PER_VARLIST3_NVAR;
+    nfipjtag_mcycle_desc[2].per_var_wind.varlist = nfipjtag_per_varlist3;
+
+    /* wait window */
+    nfipjtag_mcycle_desc[3].type = JTAG_FIP_WAIT_WIND;
+    nfipjtag_mcycle_desc[3].wait_wind.end_ustime = wait_time[1];
+    nfipjtag_mcycle_desc[3].wait_wind.silent = 1;
+};
+
+
+int32_t nfipjtagCycleStartProtocol(uint8_t address, uint8_t bus_speed)
+{
+    uint32_t wait_time[2];
+    // Set BA wait times based on bus speed.
+
+    // Note: these timings were determined empirically using a scope.
+    //
+    // The delay to allow JTAG programming to complete can be calculated as follows:
+    //
+    // The max data size is 122 bytes = 976 bits. This is processed at a rate of two bits per clock cycle
+    // (1xTMS bit + 1xTDI bit). The JTAG reprogramming clock runs at 5 MHz, so max. total time to reprogram
+    // is 97.6 microseconds. This is the programming time only: there is an additional overhead as the nanoFIP
+    // sets up the clock, transfers the program and transfers the sampled TDO value back at the end.
+
+    nfipjtag.address = address;
+
+    // Set node ID to the required address
+
+    nfipjtag_per_varlist2[0].id |= address;
+    nfipjtag_per_varlist3[0].id |= address;
+
+    // Set BA_WAIT times according bus speed
+
+    switch(bus_speed)
+    {
+        case 0: // 31.25 Kb/s
+
+            wait_time[0]          = 38700;
+            wait_time[1]          = 48700;
+            nfipjtag.cycle_period = 69700;
+            break;
+
+        case 1: // 1000 Kb/s
+
+            wait_time[0]          = 1950;
+            wait_time[1]          = 2420;
+            nfipjtag.cycle_period = 2420;
+            break;
+
+        default: // 2500 Kb/s
+
+            wait_time[0]          = ba_wait_time[0];
+            wait_time[1]          = ba_wait_time[1] + wait_time[0];
+            nfipjtag.cycle_period = 40 + wait_time[1];
+    }
+
+    if(debug_mode)
+    {
+        printf("WAIT1: %u    WAIT2: %u    Period: %u\n",
+                wait_time[0], wait_time[1],
+                nfipjtag.cycle_period);
+    }
+
+    // Initialise the FIP BA macrocycle program
+
+    nfipjtagBuildMacroCycleDesc(wait_time);
+    fipMfipInitProtocol(nfipjtag_mcycle_desc, NFIPJTAG_MCYCLE_DESC_NENTRIES);
+
+    // Initialise FIP variables
+
+    nfipjtag.rx_status           = true;
+    nfipjtag.tx_var->length_bits = 0;
+
+    // Start the FIP BA macrocycle program
+
+    if(fipMfipStartProtocol() != 0) return -1;
+
+    // Note: it is not possible to write variables before AE/LE has started
+
+    fipMfipWriteVars();
+
+    return 0;
+}
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/nfipjtag/src/nfipjtag_main.mfip.cpp b/tools/nfipjtag/nfipjtag/src/nfipjtag_main.mfip.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c8ee3137ea6df27a2ef5711cf281ecbcf33153ae
--- /dev/null
+++ b/tools/nfipjtag/nfipjtag/src/nfipjtag_main.mfip.cpp
@@ -0,0 +1,328 @@
+/*!
+ * @file   nfipjtag_main.cpp
+ * @brief  JTAG programmer for nanoFIP devices
+ * @author Stephen Page
+ * @author Michael Davis
+ */
+
+#include <cerrno>
+#include <cstdio>
+#include <cstdlib>
+#include <csignal>
+#include <stdint.h>
+#include <unistd.h>
+#include <time.h>
+
+#include <fip.mfip.h>
+#include <nfipjtag_cycle.h>
+#include <jtag.h>
+
+// Global variables
+
+bool     quiet_mode;
+bool     debug_mode;
+bool     fast_mode = false;
+uint32_t ba_wait_time[2] = { 1080, 400 };
+uint32_t silence_time_us = 0;
+
+// Static functions
+
+static void printUsage(char *program_name)
+{
+    fprintf(stderr, "\nUsage: %s [-i<INTERFACE>] [-b<BUS SPEED>] [-qdfr] [(-s | -x)] <ADDRESS> <FILE> [<FILE>...]\n"
+                    "or: %s [-i<INTERFACE>] [-b<BUS SPEED>] <ADDRESS>\n\n%s",
+                    program_name, program_name,
+                    "            -1<WAIT1>            Wait time (us) for RX ID_DAT"
+                    "            -2<WAIT2>            Wait time (us) for end of cycle, relative to WAIT1"
+                    "            -t<SILENCE_TIME>     Set silence time (us) (0-272)"
+                    "            -b<BUS SPEED>        Select bus speed: 0=31.25 Kb/s, 1=1000 Kb/s, 2=2500 Kb/s (default)\n"
+                    "            -i<INTERFACE>        FullFIP interface number (default 0)\n"
+                    "            -q                   Quiet mode - does not report progress\n"
+                    "            -d                   Debug mode - report extra debug information\n"
+                    "            -s | -x              Select SVF file format (default) or XSVF file format\n"
+                    "            -r                   Control the JTAG TRST\n"
+                    "            -f                   Fast mode - do not check TDO, unreliable but faster programming"
+                    "            <ADDRESS>            Target nanoFIP address\n"
+                    "            <FILE>               SVF or XSVF file to process\n\n");
+}
+
+// Global functions
+
+int main(int argc, char *argv[])
+{
+    int32_t              c;
+    uint32_t             nfip_address;
+
+    int32_t              retval          = 0; // initialised to suppress compiler warning
+    uint32_t             interface       = 0;
+    uint32_t             bus_speed       = 2;
+    uint32_t             trst            = 0;
+    char                *filename        = NULL;
+    enum jtag_file_type  type            = JTAG_FILE_UNSET;
+    struct FIP_config    config;
+    int                  err;
+    // Process command-line options
+
+    while((c = getopt(argc, argv, "hqdsxrf1:2:t:b:i:")) != -1)
+    {
+        switch(c)
+        {
+            case 'h':
+
+                printUsage(argv[0]);
+                return 0;
+
+            case '1':
+            case '2':
+
+                {
+                    errno = 0;
+                    uint32_t wait_time = strtoul(optarg, NULL, 0);
+                    if(errno || wait_time > 10000)
+                    {
+                        fprintf(stderr, "Invalid wait (0-10000).\n");
+                        return 1;
+                    }
+                    ba_wait_time[c-'1'] = wait_time;
+                }
+                break;
+
+            case 't':
+
+                errno     = 0;
+                silence_time_us = strtoul(optarg, NULL, 0);
+                if(errno || silence_time_us > 272)
+                {
+                    fprintf(stderr, "Invalid silence time (0-272).\n");
+                    return 1;
+                }
+                break;
+
+            case 'b':
+
+                errno     = 0;
+                bus_speed = strtoul(optarg, NULL, 0);
+                if(errno || bus_speed > 2)
+                {
+                    fprintf(stderr, "Invalid bus speed.\n");
+                    return 1;
+                }
+                break;
+
+            case 'i':
+
+                errno     = 0;
+                interface = strtol(optarg, NULL, 0);
+                if(errno || interface > 255)
+                {
+                    fprintf(stderr, "Invalid interface number.\n");
+                    return 1;
+                }
+                break;
+
+            case 'r':
+
+                trst  = 1U;
+                break;
+
+            case 'q':
+
+                quiet_mode = true;
+                break;
+
+            case 'd':
+
+                debug_mode = true;
+                break;
+
+            case 'f':
+
+                fast_mode = true;
+                break;
+
+            case 's':
+
+                if(type == JTAG_FILE_UNSET)
+                {
+                    type = JTAG_FILE_SVF;
+                }
+                else
+                {
+                    fprintf(stderr, "Options -x and -s are mutually exclusive.\n");
+                    return 1;
+                }
+                break;
+
+            case 'x':
+
+                if(type == JTAG_FILE_UNSET)
+                {
+                    type = JTAG_FILE_XSVF;
+                }
+                else
+                {
+                    fprintf(stderr, "Options -s and -x are mutually exclusive.\n");
+                    return 1;
+                }
+                break;
+        }
+    }
+
+    // Print usage information if not enough arguments were supplied
+
+    if(argc-optind < 1)
+    {
+        fprintf(stderr, "Insufficient arguments.\n");
+        printUsage(argv[0]);
+        return 1;
+    }
+
+    // Set file type to default if it was not set by command line args
+
+    if(type == JTAG_FILE_UNSET) type = JTAG_FILE_SVF;
+
+    // Get the device address
+
+    errno = 0;
+    nfip_address = strtoul(argv[optind++], NULL, 0);
+    if(errno > 0 || nfip_address == 0 || nfip_address > 255)
+    {
+        fprintf(stderr, "Invalid address.\n");
+        return 1;
+    }
+
+//    // Update FDM hard and soft config parameters
+//
+//    fipFdmSetConfigHard(interface);
+//
+//    switch(bus_speed)
+//    {
+//        case 0: // 31.5 kbps
+//
+//            fipFdmSetConfigSoft(WORLD_FIP_31, 0, 0, 100);
+//            break;
+//
+//        case 1: // 1 Mbps
+//
+//            fipFdmSetConfigSoft(WORLD_FIP_1000, 0, 0, 62);
+//            break;
+//
+//        case 2: // 2.5 Mbps
+//
+//            if(silence_time_us == 0)
+//            {
+//                fipFdmSetConfigSoft(WORLD_FIP_2500, 0, 0, 0);
+//            }
+//            else
+//            {
+//                uint32_t st = (silence_time_us - 17)/4;
+//
+//                if(debug_mode)
+//                {
+//                    printf("SILENCE_TIME: %u us -> %u\n",silence_time_us, st);
+//                }
+//
+//                fipFdmSetConfigSoft(FDM_OTHER_2500, 0, st, 50);
+//            }
+//            break;
+//    }
+
+    // Get default mfipfgc library configuration
+    if((err = fipMfipGetConfig(&config)) != 0) 
+    {
+       fprintf(stderr, "Fatal error: Trying to get default config failed: %s.\n",
+       mstrfip_strerror(err));
+       return 1;
+    }
+
+    // nfipjtag application requires internal trigger mode
+    config.hw_config.enable_int_trig = 1;
+    config.hw_config.enable_ext_trig = 0;
+
+    // Write new configuration
+    if((err = fipMfipSetConfig(&config)) != 0) 
+    {
+       fprintf(stderr, "Fatal error: Trying to overwrite defualt config failed: %s.\n",
+       mstrfip_strerror(err));
+       return 1;
+    }
+
+    // Start WorldFIP interface
+
+    if(fipMfipStartInterface() != 0)
+    {
+        fprintf(stderr, "Fatal error: Failed to start WorldFIP interface (error %d)\n", errno);
+        nfipjtagCycleCleanUp();
+        return 1;
+    }
+
+    // Set/reset TRST
+    if(1U == trst)
+    {
+        printf("Setting TRST_HIGH in interface %d, device %d", interface, nfip_address);
+
+        if(nfipjtagCycleSetTRST(nfip_address, TRST_HIGH) != 0)
+        {
+            printf("...FAILED.\n");
+            return 1;
+        } 
+    }
+    
+    // Catch signals to allow clean-up before exiting
+
+    signal(SIGHUP,  jtagSignalHandler);
+    signal(SIGINT,  jtagSignalHandler);
+    signal(SIGQUIT, jtagSignalHandler);
+    signal(SIGTERM, jtagSignalHandler);
+
+    // Reconfigure the BA macrocycle program and set up queues
+
+    if(nfipjtagCycleInit() != 0)
+    {
+        fprintf(stderr, "Fatal error: Failed to initialise WorldFIP Bus Arbitrator program (error %d)\n", errno);
+        nfipjtagCycleCleanUp();
+        return 1;
+    }
+
+    nfipjtagCycleStartProtocol(nfip_address, bus_speed);
+
+    // Process each file in turn
+
+    for( ; optind < argc; ++optind)
+    {
+        filename = argv[optind];
+
+        printf("Programming interface %d (%d Kb/s), device %d: playing %s (%s format)\n",
+               interface,
+               bus_speed == 0 ? 31 : bus_speed == 1 ? 1000 : 2500,
+               nfip_address, filename,
+               type == JTAG_FILE_SVF ? "SVF" : "XSVF");
+
+        // Perform JTAG programming
+
+        retval = jtagProg(filename, type);
+
+        if(retval != 0) break;
+    }
+
+    fipMfipStopProtocol();
+    
+    // Programming finished, set TRST low
+    if(1U == trst)
+    {
+        printf("Setting TRST_LOW in interface %d, device %d", interface, nfip_address);
+        if(nfipjtagCycleSetTRST(nfip_address, TRST_LOW) != 0)
+        {
+            printf("...FAILED.\n");
+        }
+    }
+
+    printf("\n");
+
+    // Clean up
+    nfipjtagCycleCleanUp();
+
+    return retval;
+}
+
+// EOF
\ No newline at end of file
diff --git a/tools/nfipjtag/readme.md b/tools/nfipjtag/readme.md
new file mode 100644
index 0000000000000000000000000000000000000000..0e551e8795a7ccfbb8d860918557379ac6553030
--- /dev/null
+++ b/tools/nfipjtag/readme.md
@@ -0,0 +1,32 @@
+# nfipjtag
+
+nfipjtag is a tool for programming system boards with nanofip over WorldFIP. \
+nfipjtag code is taken from fgc: \
+https://gitlab.cern.ch/pelson/fgc/-/tree/master/sw/lib/fip_fgcd/src \
+https://gitlab.cern.ch/pelson/fgc/-/tree/master/sw/utilities/unix/nfipjtag/src \
+https://ohwr.org/project/nanofip/wikis/WP10 \
+This is a modified version that can be used in any project that uses masterfip. \
+The program reads and decodes the SVF file (with gateware description) and sends TMS and TDI signals over WorldFIP to nanofip.
+
+### prerequirements
+The same as for masterfip.
+
+### building
+```make```
+
+### running
+All mandatory and optional parameters are described in help (./nfipjtag_main.mfip -h). \
+eg. \
+```sudo ./nfipjtag_main.mfip -b2 -d 1 diot_hydra2_top_svf/diot_hydra2_top_VERIFY.svf```
+
+### jtag trst
+To program the gateware correctly the trst jtag signal must be high, i.e. make sure that there is no resistor that pulls down the trst signal.
+In the FGC, the TRTS signal is driven by a modified version of nanofip (option -r). The current official version of nanoFIP does not drive the TRST line, so for reprogramming to work, the TRST signal cannot be pulled down. In future versions the nanoFIP will control TRST, so there will be no need to desolder the pull-down resistor.
+
+### fast mode (-f)
+The fast mode allows to program the gateware up to 12 times faster. \
+Fast mode checks the TDO signal once every 63 TDO signals, so it may not detect all programming error. \
+Therfore this mode is for development purposes only. \
+e.g. \
+``` sudo ./nfipjtag_main.mfip -1640 -2200 -t116 -b2 -f -d 1 diot_hydra2_top_svf/diot_hydra2_top_VERIFY.svf ``` \
+To speed up programming, the default macrocycle values might be changed to e.g. ```-1640 -2200 -t116```.
\ No newline at end of file