diff --git a/FEE/Makefile b/FEE/Makefile
new file mode 100644
index 0000000000000000000000000000000000000000..feb92ecb7df2bffec025f9e607a855dd43c8b655
--- /dev/null
+++ b/FEE/Makefile
@@ -0,0 +1,25 @@
+CC               = gcc
+SOURCEDIR	 = ./ 
+INCLUDEDIR       = ./
+BUILDDIR         = ./
+PATH            +=
+CFLAGS          := -O2 -W -Wall -Wextra #-Wno-unused #-Werror -pedantic 
+CPPFLAGS        := -I$(INCLUDEDIR)
+LDFLAGS         := -lpthread
+SOURCES         := $(wildcard *.c)
+OBJECTS         := $(patsubst %.c, $(BUILDDIR)/%.o, $(subst $(SOURCEDIR)/,, $(SOURCES)))
+TARGET          := fee_demo 
+
+DEBUG?=1
+ifeq  "$(shell expr $(DEBUG) \> 1)" "1"
+	    CFLAGS += -DDEBUGLEVEL=$(DEBUG)
+else
+	    CFLAGS += -DDEBUGLEVEL=1
+endif
+
+
+all: $(SOURCES)
+	$(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) $^ -o $(TARGET)
+
+clean:
+	rm -f $(TARGET)
diff --git a/FEE/rmap.c b/FEE/rmap.c
new file mode 100644
index 0000000000000000000000000000000000000000..6deb6a041c9f968d983fc3a6dd606e352026834e
--- /dev/null
+++ b/FEE/rmap.c
@@ -0,0 +1,744 @@
+/**
+ * @file   rmap.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief rmap command/reply helper functions
+ *
+ * @note the extended address byte is always set to 0x0
+ */
+
+
+
+#include <string.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include <rmap.h>
+
+
+
+
+
+/**
+ * @brief valiidates a command code
+ *
+ * @param cmd the command code
+ *
+ * @returns 0 on success, error otherwise
+ */
+
+static int rmap_validate_cmd_code(uint8_t cmd)
+{
+	switch (cmd) {
+	case RMAP_READ_ADDR_SINGLE:
+	case RMAP_READ_ADDR_INC:
+	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
+	case RMAP_WRITE_ADDR_SINGLE:
+	case RMAP_WRITE_ADDR_INC:
+	case RMAP_WRITE_ADDR_SINGLE_REPLY:
+	case RMAP_WRITE_ADDR_INC_REPLY:
+	case RMAP_WRITE_ADDR_SINGLE_VERIFY:
+	case RMAP_WRITE_ADDR_INC_VERIFY:
+	case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY:
+	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
+		return 0;
+	default:
+		return -1;
+	}
+}
+
+
+/**
+ * @brief get the minimum header size given the RMAP instruction
+ *
+ * @param pkt a struct rmap_pkt
+ *
+ * @returns header size or -1 on error
+ */
+
+static int rmap_get_min_hdr_size(struct rmap_pkt *pkt)
+{
+
+
+	switch (pkt->ri.cmd) {
+	case RMAP_READ_ADDR_SINGLE:
+	case RMAP_READ_ADDR_INC:
+	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
+
+		if (pkt->ri.cmd_resp)
+			return RMAP_HDR_MIN_SIZE_READ_CMD;
+
+		return RMAP_HDR_MIN_SIZE_READ_REP;
+
+	case RMAP_WRITE_ADDR_SINGLE:
+	case RMAP_WRITE_ADDR_INC:
+	case RMAP_WRITE_ADDR_SINGLE_REPLY:
+	case RMAP_WRITE_ADDR_INC_REPLY:
+	case RMAP_WRITE_ADDR_SINGLE_VERIFY:
+	case RMAP_WRITE_ADDR_INC_VERIFY:
+	case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY:
+	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
+
+		if (pkt->ri.cmd_resp)
+			return RMAP_HDR_MIN_SIZE_WRITE_CMD;
+
+		return RMAP_HDR_MIN_SIZE_WRITE_REP;
+
+	default:
+		return -1;
+	}
+}
+
+/**
+ * @brief calculate the CRC8 of a given buffer
+ *
+ * @param buf the buffer containing the data
+ * @param len the length of the buffer
+ *
+ * @returns the CRC8
+ */
+
+uint8_t rmap_crc8(const uint8_t *buf, const size_t len)
+{
+	size_t i;
+
+	uint8_t crc8 = 0;
+
+	/* crc8 lookup table from ECSS‐E‐ST‐50‐52C A.3 */
+	const uint8_t crc8_lt[256] = {
+		0x00, 0x91, 0xe3, 0x72, 0x07, 0x96, 0xe4, 0x75,
+		0x0e, 0x9f, 0xed, 0x7c, 0x09, 0x98, 0xea, 0x7b,
+		0x1c, 0x8d, 0xff, 0x6e, 0x1b, 0x8a, 0xf8, 0x69,
+		0x12, 0x83, 0xf1, 0x60, 0x15, 0x84, 0xf6, 0x67,
+		0x38, 0xa9, 0xdb, 0x4a, 0x3f, 0xae, 0xdc, 0x4d,
+		0x36, 0xa7, 0xd5, 0x44, 0x31, 0xa0, 0xd2, 0x43,
+		0x24, 0xb5, 0xc7, 0x56, 0x23, 0xb2, 0xc0, 0x51,
+		0x2a, 0xbb, 0xc9, 0x58, 0x2d, 0xbc, 0xce, 0x5f,
+		0x70, 0xe1, 0x93, 0x02, 0x77, 0xe6, 0x94, 0x05,
+		0x7e, 0xef, 0x9d, 0x0c, 0x79, 0xe8, 0x9a, 0x0b,
+		0x6c, 0xfd, 0x8f, 0x1e, 0x6b, 0xfa, 0x88, 0x19,
+		0x62, 0xf3, 0x81, 0x10, 0x65, 0xf4, 0x86, 0x17,
+		0x48, 0xd9, 0xab, 0x3a, 0x4f, 0xde, 0xac, 0x3d,
+		0x46, 0xd7, 0xa5, 0x34, 0x41, 0xd0, 0xa2, 0x33,
+		0x54, 0xc5, 0xb7, 0x26, 0x53, 0xc2, 0xb0, 0x21,
+		0x5a, 0xcb, 0xb9, 0x28, 0x5d, 0xcc, 0xbe, 0x2f,
+		0xe0, 0x71, 0x03, 0x92, 0xe7, 0x76, 0x04, 0x95,
+		0xee, 0x7f, 0x0d, 0x9c, 0xe9, 0x78, 0x0a, 0x9b,
+		0xfc, 0x6d, 0x1f, 0x8e, 0xfb, 0x6a, 0x18, 0x89,
+		0xf2, 0x63, 0x11, 0x80, 0xf5, 0x64, 0x16, 0x87,
+		0xd8, 0x49, 0x3b, 0xaa, 0xdf, 0x4e, 0x3c, 0xad,
+		0xd6, 0x47, 0x35, 0xa4, 0xd1, 0x40, 0x32, 0xa3,
+		0xc4, 0x55, 0x27, 0xb6, 0xc3, 0x52, 0x20, 0xb1,
+		0xca, 0x5b, 0x29, 0xb8, 0xcd, 0x5c, 0x2e, 0xbf,
+		0x90, 0x01, 0x73, 0xe2, 0x97, 0x06, 0x74, 0xe5,
+		0x9e, 0x0f, 0x7d, 0xec, 0x99, 0x08, 0x7a, 0xeb,
+		0x8c, 0x1d, 0x6f, 0xfe, 0x8b, 0x1a, 0x68, 0xf9,
+		0x82, 0x13, 0x61, 0xf0, 0x85, 0x14, 0x66, 0xf7,
+		0xa8, 0x39, 0x4b, 0xda, 0xaf, 0x3e, 0x4c, 0xdd,
+		0xa6, 0x37, 0x45, 0xd4, 0xa1, 0x30, 0x42, 0xd3,
+		0xb4, 0x25, 0x57, 0xc6, 0xb3, 0x22, 0x50, 0xc1,
+		0xba, 0x2b, 0x59, 0xc8, 0xbd, 0x2c, 0x5e, 0xcf,
+	};
+
+
+
+	if (!buf)
+		return 0;
+
+
+	for (i = 0; i < len; i++)
+		crc8 = crc8_lt[crc8 ^ buf[i]];
+
+	return crc8;
+}
+
+
+/**
+ * @brief create an RMAP packet and set defaults
+ *
+ *
+ * @note initialises protocol id to 1 and all others to 0
+ *
+ * @returns a struct rmap_pkt or NULL on error
+ */
+
+struct rmap_pkt *rmap_create_packet(void)
+{
+	struct rmap_pkt *pkt;
+
+
+	pkt = (struct rmap_pkt *) calloc(sizeof(struct rmap_pkt), 1);
+	if (pkt)
+		pkt->proto_id = RMAP_PROTOCOL_ID;
+
+	return pkt;
+}
+
+
+/**
+ * @brief destroys an RMAP packet
+ *
+ * @param pkt a struct rmap_pkt
+ *
+ * @note this will NOT deallocate and pointer references assigned by the user
+ */
+
+void rmap_destroy_packet(struct rmap_pkt *pkt)
+{
+	free(pkt);
+}
+
+
+/**
+ * @brief completely destroys an RMAP packet
+ *
+ * @param pkt a struct rmap_pkt
+ *
+ * @note this will attempt to deallocate any pointer references assigned by the
+ * 	 user
+ * @warning use with care
+ */
+
+void rmap_erase_packet(struct rmap_pkt *pkt)
+{
+	free(pkt->path);
+	free(pkt->rpath);
+	free(pkt->data);
+	free(pkt);
+}
+
+/**
+ * @brief set the destination (target) logical address
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param addr	the destination logical address
+ */
+
+void rmap_set_dst(struct rmap_pkt *pkt, uint8_t addr)
+{
+	if (pkt)
+		pkt->dst = addr;
+}
+
+
+/**
+ * @brief set the source (initiator) logical address
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param addr	the source logical address
+ */
+
+void rmap_set_src(struct rmap_pkt *pkt, uint8_t addr)
+{
+	if (pkt)
+		pkt->src = addr;
+}
+
+
+/**
+ * @brief set the command authorisation key
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param key	the authorisation key
+ */
+
+void rmap_set_key(struct rmap_pkt *pkt, uint8_t key)
+{
+	if (pkt)
+		pkt->key = key;
+}
+
+
+/**
+ * @brief set the reply address path
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param rpath the reply path
+ * @param len   the number of elements in the reply path (multiple of 4)
+ *
+ * @note see ECSS‐E‐ST‐50‐52C 5.1.6 for return path rules
+ *
+ * @returns 0 on success, -1 on error
+ */
+
+int rmap_set_reply_path(struct rmap_pkt *pkt, const uint8_t *rpath, uint8_t len)
+{
+	if (!pkt)
+		return -1;
+
+	if (!rpath && len)
+		return -1;
+
+	if (len > RMAP_MAX_REPLY_PATH_LEN)
+		return -1;
+
+	if (len & 0x3)
+		return -1;
+
+	pkt->rpath_len = len;
+
+	pkt->rpath = (uint8_t *) malloc(pkt->rpath_len);
+	if (!pkt->rpath)
+		return -1;
+
+	memcpy(pkt->rpath, rpath, pkt->rpath_len);
+
+	/* number of 32 bit words needed to contain the path */
+	pkt->ri.reply_addr_len = len >> 2;
+
+	return 0;
+}
+
+
+/**
+ * @brief set the destination address path
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param path the destination path
+ * @param len   the number of elements in the destination path
+ *
+ * @note see ECSS‐E‐ST‐50‐52C 5.1.6 for return path rules
+ *
+ * @returns 0 on success, -1 on error
+ */
+
+int rmap_set_dest_path(struct rmap_pkt *pkt, const uint8_t *path, uint8_t len)
+{
+	if (!pkt)
+		return -1;
+
+	if (!path && len)
+		return -1;
+
+	if (len > RMAP_MAX_PATH_LEN)
+		return -1;
+
+	pkt->path_len = len;
+
+	pkt->path = (uint8_t *) malloc(pkt->path_len);
+	if (!pkt->path)
+		return -1;
+
+	memcpy(pkt->path, path, pkt->path_len);
+
+	return 0;
+}
+
+
+/**
+ * @brief set an RMAP command
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param cmd	the selected command
+ *
+ * @param returns -1 on error
+ */
+
+int rmap_set_cmd(struct rmap_pkt *pkt, uint8_t cmd)
+{
+	if (!pkt)
+		return -1;
+
+	if (rmap_validate_cmd_code(cmd))
+		return -1;
+
+
+	pkt->ri.cmd      = cmd & 0xF;
+	pkt->ri.cmd_resp = 1;
+
+	return 0;
+}
+
+
+/**
+ * @brief set an RMAP transaction identifier
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param id	the transaction identifier
+ */
+
+void rmap_set_tr_id(struct rmap_pkt *pkt, uint16_t id)
+{
+	if (!pkt)
+		return;
+
+	pkt->tr_id = id;
+}
+
+
+/**
+ * @brief set a data address
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param addr	the address
+ */
+
+void rmap_set_data_addr(struct rmap_pkt *pkt, uint32_t addr)
+{
+	if (!pkt)
+		return;
+
+	pkt->addr = addr;
+}
+
+/**
+ * @brief set an RMAP command
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param len	the data length (in bytes)
+ *
+ * @param returns -1 on error
+ *
+ * @note the length is at most 2^24-1 bytes
+ * @note if the RMAP command is of 'SINGLE' type, only multiples of 4
+ *	 will result in successfull execution of the command (at least
+ *	 with the GRSPW2 core)
+ */
+
+int rmap_set_data_len(struct rmap_pkt *pkt, uint32_t len)
+{
+	if (!pkt)
+		return -1;
+
+	if (len > RMAP_MAX_DATA_LEN)
+		return -1;
+
+	pkt->data_len = len;
+
+	return 0;
+}
+
+
+/**
+ * @brief build an rmap header
+ *
+ * @param pkt	a struct rmap_pkt
+ * @param hdr	the header buffer; if NULL, the function returns the needed size
+ *
+ * @returns -1 on error, size of header otherwise
+ */
+
+int rmap_build_hdr(struct rmap_pkt *pkt, uint8_t *hdr)
+{
+	int i;
+	int n = 0;
+
+
+	if (!pkt)
+		return -1;
+
+	if (!hdr) {
+		n = rmap_get_min_hdr_size(pkt);
+		n += pkt->path_len;
+		n += pkt->rpath_len;
+		return n;
+	}
+
+
+	for (i = 0; i < pkt->path_len; i++)
+		hdr[n++] = pkt->path[i];	/* routing path to target */
+
+	hdr[n++] = pkt->dst;			/* target logical address */
+	hdr[n++] = pkt->proto_id;		/* protocol id */
+	hdr[n++] = pkt->instruction;		/* instruction */
+	hdr[n++] = pkt->key;			/* key/status */
+
+	for (i = 0; i < pkt->rpath_len; i++)
+		hdr[n++] = pkt->rpath[i];	/* return path to source */
+
+	hdr[n++] = pkt->src;			/* source logical address */
+	hdr[n++] = (uint8_t) (pkt->tr_id >> 8);	/* MSB of transaction id */
+	hdr[n++] = (uint8_t)  pkt->tr_id;	/* LSB of transaction id */
+
+
+	/* commands have a data address */
+	if (pkt->ri.cmd_resp) {
+		hdr[n++] = 0x0;	/* extended address field (unused) */
+		hdr[n++] = (uint8_t) (pkt->addr >> 24); /* data addr MSB */
+		hdr[n++] = (uint8_t) (pkt->addr >> 16);
+		hdr[n++] = (uint8_t) (pkt->addr >>  8);
+		hdr[n++] = (uint8_t)  pkt->addr;	/* data addr LSB */
+	} else if (!pkt->ri.cmd_resp && pkt->ri.cmd & RMAP_CMD_BIT_WRITE) {
+		/* all headers have data length unless they are a write reply */
+		return n;
+	} else {
+		hdr[n++] = 0x0;	/* on other replies, this is a reserved field */
+	}
+
+	hdr[n++] = (uint8_t) (pkt->data_len >> 16); /* data len MSB */
+	hdr[n++] = (uint8_t) (pkt->data_len >>  8);
+	hdr[n++] = (uint8_t)  pkt->data_len;	    /* data len LSB */
+
+	return n;
+}
+
+
+/**
+ * @brief create an rmap packet from a buffer
+ *
+ * @param buf the buffer, with the target path stripped away, i.e.
+ *	  starting with <logical address>, <protocol id>, ...
+ *
+ * @note there is no size checking, be careful
+ *
+ * @returns an rmap packet, containing the decoded buffer including any data,
+ *	    NULL on error
+ */
+
+struct rmap_pkt *rmap_pkt_from_buffer(uint8_t *buf)
+{
+	size_t n = 0;
+	size_t i;
+
+	struct rmap_pkt *pkt = NULL;
+
+
+	if (!buf)
+		goto error;
+
+	if (buf[RMAP_PROTOCOL_ID] != RMAP_PROTOCOL_ID) {
+		printf("Not an RMAP packet, got %x but expected %x\n",
+		       buf[RMAP_PROTOCOL_ID], RMAP_PROTOCOL_ID);
+		goto error;
+	}
+
+	pkt = rmap_create_packet();
+	if (!pkt) {
+		printf("Error creating packet\n");
+		goto error;
+	}
+
+	pkt->dst         = buf[RMAP_DEST_ADDRESS];
+	pkt->proto_id    = buf[RMAP_PROTOCOL_ID];
+	pkt->instruction = buf[RMAP_INSTRUCTION];
+	pkt->key         = buf[RMAP_CMD_DESTKEY];
+
+
+	if (pkt->ri.cmd_resp) {
+		pkt->rpath_len = pkt->ri.reply_addr_len << 2;
+
+		pkt->rpath = (uint8_t *) malloc(pkt->rpath_len);
+		if (!pkt->rpath)
+			goto error;
+
+		for (i = 0; i < pkt->rpath_len; i++)
+			pkt->rpath[i] = buf[RMAP_REPLY_ADDR_START + i];
+
+
+		n = pkt->rpath_len; /* rpath skip */
+	}
+
+	pkt->src   = buf[RMAP_SRC_ADDR + n];
+	pkt->tr_id = ((uint16_t) buf[RMAP_TRANS_ID_BYTE0 + n] << 8) |
+	              (uint16_t) buf[RMAP_TRANS_ID_BYTE1 + n];
+
+	/* commands have a data address */
+	if (pkt->ri.cmd_resp) {
+		pkt->addr = ((uint32_t) buf[RMAP_ADDR_BYTE0 + n] << 24) |
+			    ((uint32_t) buf[RMAP_ADDR_BYTE1 + n] << 16) |
+			    ((uint32_t) buf[RMAP_ADDR_BYTE2 + n] <<  8) |
+			     (uint32_t) buf[RMAP_ADDR_BYTE3 + n];
+		n += 4; /* addr skip, extended byte is incorporated in define */
+	}
+
+	/* all headers have data length unless they are a write reply */
+	if (!(!pkt->ri.cmd_resp && (pkt->ri.cmd & (RMAP_CMD_BIT_WRITE)))) {
+
+		pkt->data_len = ((uint32_t) buf[RMAP_DATALEN_BYTE0 + n] << 16) |
+				((uint32_t) buf[RMAP_DATALEN_BYTE1 + n] <<  8) |
+			         (uint32_t) buf[RMAP_DATALEN_BYTE2 + n];
+	}
+
+	if (pkt->data_len) {
+		pkt->data = (uint8_t *) malloc(pkt->data_len);
+		if (!pkt->data)
+			goto error;
+
+		for (i = 0; i < pkt->data_len; i++)
+			pkt->data[i] = buf[RMAP_DATA_START + n + i];
+	}
+
+
+	return pkt;
+
+error:
+	if (pkt) {
+		free(pkt->data);
+		free(pkt->rpath);
+		free(pkt);
+	}
+
+	return NULL;
+}
+
+
+
+/**** UNFINISHED INFO STUFF BELOW ******/
+
+__extension__
+static int rmap_check_status(uint8_t status)
+{
+
+
+	printf("\tStatus: ");
+
+	switch (status) {
+	case RMAP_STATUS_SUCCESS:
+		printf("Command executed successfully");
+		break;
+	case RMAP_STATUS_GENERAL_ERROR:
+		printf("General error code");
+		break;
+	case RMAP_STATUS_UNUSED_TYPE_OR_CODE:
+		printf("Unused RMAP Packet Type or Command Code");
+		break;
+	case RMAP_STATUS_INVALID_KEY:
+		printf("Invalid key");
+		break;
+	case RMAP_STATUS_INVALID_DATA_CRC:
+		printf("Invalid Data CRC");
+		break;
+	case RMAP_STATUS_EARLY_EOP:
+		printf("Early EOP");
+		break;
+	case RMAP_STATUS_TOO_MUCH_DATA:
+		printf("Too much data");
+		break;
+	case RMAP_STATUS_EEP:
+		printf("EEP");
+		break;
+	case RMAP_STATUS_RESERVED:
+		printf("Reserved");
+		break;
+	case RMAP_STATUS_VERIFY_BUFFER_OVERRRUN:
+		printf("Verify buffer overrrun");
+		break;
+	case RMAP_STATUS_CMD_NOT_IMPL_OR_AUTH:
+		printf("RMAP Command not implemented or not authorised");
+		break;
+	case RMAP_STATUS_RMW_DATA_LEN_ERROR:
+		printf("RMW Data Length error");
+		break;
+	case RMAP_STATUS_INVALID_TARGET_LOGICAL_ADDR:
+		printf("Invalid Target Logical Address");
+		break;
+	default:
+		printf("Reserved unused error code %d", status);
+		break;
+	}
+
+	printf("\n");
+
+
+	return status;
+}
+
+
+
+static void rmap_process_read_reply(uint8_t *pkt)
+{
+	uint32_t i;
+
+	uint32_t len = 0;
+
+
+	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE0]) << 16;
+	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE1]) <<  8;
+	len |= ((uint32_t) pkt[RMAP_DATALEN_BYTE2]) <<  0;
+
+	printf("\tData length is %u bytes:\n\t", len);
+
+
+	for (i = 0; i < len; i++)
+		printf("%02x:", pkt[RMAP_DATA_START + i]);
+
+	printf("\b \n");
+}
+
+
+
+
+static void rmap_parse_cmd_pkt(uint8_t *pkt)
+{
+	(void) pkt;
+	printf("\trmap_parse_cmd_pkt() not implemented\n");
+}
+
+
+static void rmap_parse_reply_pkt(uint8_t *pkt)
+{
+	struct rmap_instruction *ri;
+
+
+	ri = (struct rmap_instruction *) &pkt[RMAP_INSTRUCTION];
+
+	printf("\tInstruction: ");
+
+	switch (ri->cmd) {
+
+	case RMAP_READ_ADDR_SINGLE:
+		printf("Read single address\n");
+		rmap_process_read_reply(pkt);
+		break;
+	case RMAP_READ_ADDR_INC:
+		printf("Read incrementing address\n");
+		rmap_process_read_reply(pkt);
+		break;
+	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
+		printf("RMW incrementing address verify reply\n");
+		break;
+	case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
+		printf("Write incrementing address verify reply\n");
+		break;
+	case RMAP_WRITE_ADDR_INC_REPLY:
+		printf("Write incrementing address reply\n");
+		break;
+	default:
+		printf("decoding of instruction 0x%02X not implemented\n",
+		       ri->cmd);
+		break;
+	}
+}
+
+
+/**
+ * parse an RMAP packet:
+ *
+ * expected format: <logical address> <protocol id> ...
+ */
+
+void rmap_parse_pkt(uint8_t *pkt)
+{
+	struct rmap_instruction *ri;
+
+	if (pkt[RMAP_PROTOCOL_ID] != RMAP_PROTOCOL_ID) {
+		printf("\nNot an RMAP packet, got %x but expected %x\n",
+		       pkt[RMAP_PROTOCOL_ID], RMAP_PROTOCOL_ID);
+		return;
+	}
+
+
+	ri = (struct rmap_instruction *) &pkt[RMAP_INSTRUCTION];
+
+	if (ri->cmd_resp) {
+		printf("This is a command packet\n");
+		if (!rmap_check_status(pkt[RMAP_REPLY_STATUS]))
+			rmap_parse_cmd_pkt(pkt);
+	} else {
+		printf("This is a reply packet\n");
+		if (!rmap_check_status(pkt[RMAP_REPLY_STATUS]))
+			rmap_parse_reply_pkt(pkt);
+	}
+}
+
diff --git a/FEE/rmap.h b/FEE/rmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..2d94db27be11bd63d2ab032ae6530d6f9817f230
--- /dev/null
+++ b/FEE/rmap.h
@@ -0,0 +1,215 @@
+/**
+ * @file   rmap.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief rmap command/reply helper functions
+ */
+
+#ifndef RMAP_H
+#define RMAP_H
+
+#include <stdint.h>
+
+/**
+ * valid RMAP command codes, see Table 5-1 of ECSS‐E‐ST‐50‐52C
+ *
+ * all valid commands are made up of the four bits below
+ */
+
+
+#define RMAP_CMD_BIT_WRITE			0x8
+#define RMAP_CMD_BIT_VERIFY			0x4
+#define RMAP_CMD_BIT_REPLY			0x2
+#define RMAP_CMD_BIT_INC			0x1
+
+#define RMAP_READ_ADDR_SINGLE			0x2
+#define RMAP_READ_ADDR_INC			0x3
+#define RMAP_READ_MODIFY_WRITE_ADDR_INC		0x7
+#define RMAP_WRITE_ADDR_SINGLE			0x8
+#define RMAP_WRITE_ADDR_INC			0x9
+#define RMAP_WRITE_ADDR_SINGLE_REPLY		0xa
+#define RMAP_WRITE_ADDR_INC_REPLY		0xb
+#define RMAP_WRITE_ADDR_SINGLE_VERIFY		0xc
+#define RMAP_WRITE_ADDR_INC_VERIFY		0xd
+#define RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY	0xe
+#define RMAP_WRITE_ADDR_INC_VERIFY_REPLY	0xf
+
+/**
+ * RMAP error and status codes, see Table 5-4 of ECSS‐E‐ST‐50‐52C
+ */
+
+#define RMAP_STATUS_SUCCESS			0x0
+#define RMAP_STATUS_GENERAL_ERROR		0x1
+#define RMAP_STATUS_UNUSED_TYPE_OR_CODE		0x2
+#define RMAP_STATUS_INVALID_KEY			0x3
+#define RMAP_STATUS_INVALID_DATA_CRC		0x4
+#define RMAP_STATUS_EARLY_EOP			0x5
+#define RMAP_STATUS_TOO_MUCH_DATA		0x6
+#define RMAP_STATUS_EEP				0x7
+#define RMAP_STATUS_RESERVED			0x8
+#define RMAP_STATUS_VERIFY_BUFFER_OVERRRUN	0x9
+#define RMAP_STATUS_CMD_NOT_IMPL_OR_AUTH	0xa
+#define RMAP_STATUS_RMW_DATA_LEN_ERROR		0xb
+#define RMAP_STATUS_INVALID_TARGET_LOGICAL_ADDR	0xc
+
+
+/**
+ * RMAP minimum header sizes, see ECSS‐E‐ST‐50‐52C
+ */
+
+#define RMAP_HDR_MIN_SIZE_WRITE_CMD		15
+#define RMAP_HDR_MIN_SIZE_WRITE_REP		 7
+
+#define RMAP_HDR_MIN_SIZE_READ_CMD		RMAP_HDR_MIN_SIZE_WRITE_CMD
+#define RMAP_HDR_MIN_SIZE_READ_REP		11
+
+#define RMAP_HDR_MIN_SIZE_RMW_CMD		RMAP_HDR_MIN_SIZE_READ_CMD
+#define RMAP_HDR_MIN_SIZE_RMW_REP		RMAP_HDR_MIN_SIZE_READ_REP
+
+
+
+/* RMAP header bytes in relative offsets following last entry in target path */
+#define RMAP_DEST_ADDRESS	0x00
+#define RMAP_PROTOCOL_ID	0x01
+#define RMAP_INSTRUCTION	0x02
+#define RMAP_CMD_DESTKEY	0x03
+#define RMAP_REPLY_STATUS	RMAP_CMD_DESTKEY
+#define RMAP_REPLY_ADDR_START	0x04	/* optional */
+
+/* RMAP header bytes in relative offsets, add (reply address length * 4) */
+#define RMAP_SRC_ADDR		0x04
+#define RMAP_TRANS_ID_BYTE0	0x05
+#define RMAP_TRANS_ID_BYTE1	0x06
+
+/* depending on the command, this is 0 or may contain an address extension */
+#define RMAP_RESERVED		0x07
+#define RMAP_EXTENDED		RMAP_RESERVED
+
+/* optional RMAP header bytes in relative offsets */
+#define RMAP_ADDR_BYTE0		0x08
+#define RMAP_ADDR_BYTE1		0x09
+#define RMAP_ADDR_BYTE2		0x0a
+#define RMAP_ADDR_BYTE3		0x0b
+
+/* RMAP header bytes in relative offsets (add extra 4 if address present)  */
+#define RMAP_DATALEN_BYTE0	0x08
+#define RMAP_DATALEN_BYTE1	0x09
+#define RMAP_DATALEN_BYTE2	0x0a
+#define RMAP_HEADER_CRC		0x0b
+#define RMAP_DATA_START		0x0c
+
+/**
+ * While the size of a SpW packet is in principl not limited, the size of the
+ * header cannot be more than 255 bytes given the 8-bit width of the transfer
+ * descriptor's HEADERLEN field in the GRSPW2 core, so we'll use that as our
+ * limit.
+ *
+ * The reply address path can be at most 12 bytes, as the reply address length
+ * field in the RMAP instruction field is only 2 bits wide and counts the
+ * number of 32 bit words needed to hold the return path.
+ *
+ * The maximum data length is 2^24-1 bits
+ *
+ * All other fields in the header (not counting the header CRC) amount to
+ * 27 bytes.
+ *
+ * @see GR712RC-UM v2.7 p112 and ECSS‐E‐ST‐50‐52C e.g. 5.3.1.1
+ */
+
+#define RMAP_MAX_PATH_LEN		 15
+#define RMAP_MAX_REPLY_ADDR_LEN		  3
+#define RMAP_MAX_REPLY_PATH_LEN		 12
+#define RMAP_MAX_DATA_LEN	   0xFFFFFFUL
+
+
+
+__extension__
+struct rmap_instruction {
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint8_t	reserved:1;
+	uint8_t	cmd_resp:1;
+	uint8_t cmd:4;
+	uint8_t	reply_addr_len:2;
+#elif (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+	uint8_t	reply_addr_len:2;
+	uint8_t cmd:4;
+	uint8_t	cmd_resp:1;
+	uint8_t	reserved:1;
+#else
+#error "Unknown byte order"
+#endif
+}__attribute__((packed));
+#if 0
+compile_time_assert((sizeof(struct rmap_instruction) == sizeof(uint8_t),
+		    RMAP_INSTRUCTION_STRUCT_WRONG_SIZE));
+#endif
+
+
+/**
+ * This structure holds the relevant contents of an RMAP packet.
+ *
+ * @note this is NOT an actual RMAP packet!
+ */
+
+__extension__
+struct rmap_pkt {
+	uint8_t		*path;		/* path to SpW target */
+	uint8_t		path_len;	/* entries in the path */
+	uint8_t		dst;		/* target logical address */
+	uint8_t		proto_id;	/* protoco id (0x1 = RMAP */
+	union {
+		struct rmap_instruction ri;
+		uint8_t	instruction;
+	};
+	union {
+		uint8_t	key;		/* command authorisation key */
+		uint8_t	status;		/* reply error/status codes */
+	};
+	uint8_t		src;		/* initiator logical address */
+	uint8_t		*rpath;		/* reply path */
+	uint8_t		rpath_len;	/* entries in the reply path */
+	uint16_t	tr_id;		/* transaction identifier */
+	uint32_t	addr;		/* (first) data address */
+	uint8_t		*data;
+	uint32_t	data_len;	/* lenght of data in bytes */
+};
+
+
+
+uint8_t rmap_crc8(const uint8_t *buf, const size_t len);
+
+struct rmap_pkt *rmap_create_packet(void);
+struct rmap_pkt *rmap_pkt_from_buffer(uint8_t *buf);
+int rmap_build_hdr(struct rmap_pkt *pkt, uint8_t *hdr);
+int rmap_set_data_len(struct rmap_pkt *pkt, uint32_t len);
+void rmap_set_data_addr(struct rmap_pkt *pkt, uint32_t addr);
+int rmap_set_cmd(struct rmap_pkt *pkt, uint8_t cmd);
+
+
+void rmap_set_dst(struct rmap_pkt *pkt, uint8_t addr);
+void rmap_set_src(struct rmap_pkt *pkt, uint8_t addr);
+void rmap_set_key(struct rmap_pkt *pkt, uint8_t key);
+void rmap_set_tr_id(struct rmap_pkt *pkt, uint16_t id);
+
+int rmap_set_reply_path(struct rmap_pkt *pkt, const uint8_t *rpath, uint8_t len);
+int rmap_set_dest_path(struct rmap_pkt *pkt, const uint8_t *path, uint8_t len);
+
+
+void rmap_erase_packet(struct rmap_pkt *pkt);
+
+
+void rmap_parse_pkt(uint8_t *pkt);
+
+
+#endif /* RMAP_H */
diff --git a/FEE/smile_fee.c b/FEE/smile_fee.c
new file mode 100644
index 0000000000000000000000000000000000000000..3866b8a408f6f50cf67ece98338e7da2cb94abd0
--- /dev/null
+++ b/FEE/smile_fee.c
@@ -0,0 +1,98 @@
+/**
+ * @file   smile_fee.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2020
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE interface
+ *
+ * @warn byte order is BIG ENDIAN!
+ *
+ */
+
+
+
+#include <stdio.h>
+
+
+#include <smile_fee.h>
+
+
+/**
+ * @brief write the contents of an event package to the console
+ *
+ * @param pkt a struct fee_display_event
+ */
+
+void fee_display_event(const struct fee_event_dection *pkt)
+{
+	ssize_t i, j;
+
+
+	if (!pkt)
+		return;
+
+	printf("\n\tCOL %d ROW %d\n", pkt->col, pkt->row);
+
+	/* as per MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-91  tbl 8-11,
+	 * the upper left pixel is the last pixel in the data
+	 * and the lower left pixel is the first pixel in the data
+	 */
+	for (i = FEE_EV_ROWS - 1; i >= 0; i--) {
+
+		printf("\t");
+
+		for (j = 0; j <  FEE_EV_COLS; j++)
+			printf("%05d ", pkt->pix[j + i * FEE_EV_COLS]);
+
+		printf("\n");
+	}
+
+	printf("\n");
+}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+void test_fee_display_event(void)
+{
+	ssize_t i, j;
+	struct fee_event_dection pkt;
+
+
+	pkt.col = 12;
+	pkt.row = 43;
+
+	for (i = FEE_EV_ROWS - 1; i >= 0; i--)
+		for (j = 0; j <  FEE_EV_COLS; j++)
+			pkt.pix[j + i * FEE_EV_COLS] = j + i * FEE_EV_COLS;
+
+	fee_display_event(&pkt);
+}
+
+#if 0
+int main(void)
+{
+	test_fee_display_event();
+}
+#endif
diff --git a/FEE/smile_fee.h b/FEE/smile_fee.h
new file mode 100644
index 0000000000000000000000000000000000000000..416901ca5775970f0f6017c91c69135b211e17ec
--- /dev/null
+++ b/FEE/smile_fee.h
@@ -0,0 +1,141 @@
+/**
+ * @file   smile_fee.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2020
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE interface
+ *
+ * @warn byte order is BIG ENDIAN!
+ */
+
+#ifndef SMILE_FEE_H
+#define SMILE_FEE_H
+
+#include <stdint.h>
+
+
+
+/**
+ *  @see MSSL-SMILE-SXI-IRD-0001
+ */
+
+#define DPU_LOGICAL_ADDRESS	0x50
+#define FEE_LOGICAL_ADDRESS	0x51
+
+#define RMAP_PROTOCOL_ID	0x01
+#define FEE_DATA_PROTOCOL	0xF0	/* MSSL-IF-95 */
+
+
+
+/**
+ * FEE modes
+ *
+ * @see MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-17
+ */
+
+
+#define FEE_MODE_ID_ON		0x0	/* the thing is switched on */
+#define FEE_MODE_ID_FTP		0x1	/* frame transfer pattern */
+#define FEE_MODE_ID_STBY	0x2	/* stand-by-mode */
+#define FEE_MODE_ID_FT		0x3	/* frame transfer */
+#define FEE_MODE_ID_FF		0x4	/* full frame */
+#define FEE_MODE_ID_PTP1	0x5	/* parallel trap pump mode 1 */
+#define FEE_MODE_ID_PTP2	0x6	/* parallel trap pump mode 2 */
+#define FEE_MODE_ID_STP1	0x7	/* serial trap pump mode 1 */
+#define FEE_MODE_ID_STP2	0x8	/* serial trap pump mode 2 */
+
+
+/* @see MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-97 */
+
+#define FEE_CCD_SIDE_LEFT	0x0	/* side F */
+#define FEE_CCD_SIDE_RIGHT	0x1	/* side E */
+#define FEE_CCD_INTERLEAVED	0x2	/* F and E inverleaved */
+
+#define FEE_CDD_ID_2		0x0
+#define FEE_CDD_ID_4		0x1
+
+#define FEE_PKT_TYPE_DATA	0x0	/* any data */
+#define FEE_PKT_TYPE_EV_DET	0x1	/* event detection */
+#define FEE_PKT_TYPE_HK		0x2	/* housekeeping */
+
+
+
+/* @see MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-97 */
+struct fee_pkt_type {
+#if (__BYTE_ORDER__ == __ORDER_BIG_ENDIAN__)
+	uint16_t reserved0:4;
+	uint16_t fee_mode:4;
+	uint16_t last_pkt:1;	/* if set: last packet in readout cycle */
+	uint16_t ccd_side:2;
+	uint16_t ccd_id:1;
+	uint16_t reserved1:2;
+	uint16_t pkt_type:2;
+#elif (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__)
+	uint16_t pkt_type:2;
+	uint16_t reserved1:2;
+	uint16_t ccd_id:1;
+	uint16_t ccd_side:2;
+	uint16_t last_pkt:1;	/* if set: last packet in readout cycle */
+	uint16_t fee_mode:4;
+	uint16_t reserved0:4;
+#endif
+} __attribute__((packed));
+
+/* XXX need proper include file in path for compile time size checks */
+#if 0
+compile_time_assert((sizeof(struct fee_data_hdr)
+                     == sizeof(uint16_t)),
+                    FEE_PKT_TYPE_STRUCT_WRONG_SIZE);
+#endif
+
+
+/* @see MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-91 */
+__extension__
+struct fee_data_hdr {
+	uint8_t logical_addr;
+	uint8_t proto_id;
+	uint16_t data_len;
+	union {
+		uint16_t fee_pkt_type;
+		struct fee_pkt_type type;
+	};
+	uint16_t frame_cntr;
+	uint16_t seq_cntr;
+} __attribute__((packed));
+
+
+/* @see MSSL-SMILE-SXI-IRD-0001, req. MSSL-IF-91 */
+#define FEE_EV_COLS		 5
+#define FEE_EV_ROWS		 5
+#define FEE_EV_DET_PIXELS	25	/* 5x5 grid around event pixel */
+#define FEE_EV_PIXEL_IDX	12	/* the index of the event pixel */
+
+__extension__
+struct fee_event_dection {
+	struct fee_data_hdr hdr;
+
+	uint16_t col;
+	uint16_t row;
+
+	uint16_t pix[FEE_EV_DET_PIXELS];
+
+} __attribute__((packed));
+
+
+
+
+
+
+
+
+#endif /* SMILE_FEE_H */
diff --git a/FEE/smile_fee_cfg.h b/FEE/smile_fee_cfg.h
new file mode 100644
index 0000000000000000000000000000000000000000..ea22308465746dce6beed48917f1c0a2053e6100
--- /dev/null
+++ b/FEE/smile_fee_cfg.h
@@ -0,0 +1,45 @@
+/**
+ * @file   smile_fee_cfg.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2020
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE usage demonstrator configuration
+ */
+
+#ifndef _SMILE_FEE_CFG_H_
+#define _SMILE_FEE_CFG_H_
+
+#include <stdint.h>
+
+
+#define DPU_ADDR	0x28
+#define FEE_ADDR	0x5A
+
+
+#define DPATH	{FEE_ADDR}
+#define RPATH	{0x00, 0x00, 0x00, DPU_ADDR}
+#define DPATH_LEN	1
+#define RPATH_LEN	4
+
+/* default FEE destkey */
+#define FEE_DEST_KEY	0x0
+
+
+/* set header size maximum so we can fit the largest possible rmap commands
+ * given the hardware limit (HEADERLEN is 8 bits wide, see GR712RC UM, p. 112)
+ */
+#define HDR_SIZE	0xFF
+
+
+
+#endif /* _SMILE_FEE_CFG_H_ */
diff --git a/FEE/smile_fee_cmd.c b/FEE/smile_fee_cmd.c
new file mode 100644
index 0000000000000000000000000000000000000000..ef7b742e6d550c412114bc620b713a252315930a
--- /dev/null
+++ b/FEE/smile_fee_cmd.c
@@ -0,0 +1,600 @@
+/**
+ * @file   fee_cmd.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE RMAP command library
+ * @see SMILE FEE to DPU Interface Requirements Document MSSL-SMILE-SXI-IRD-0001
+ *
+ * @note I realise that the actual register accessors are somewhat redundant
+ *	 since they only reference the registers by a numeric id and could be
+ *	 converted into a single function in principle. This file is however
+ *	 an adaptation from code for the PLATO RDUC, so this was the easiest
+ *	 approach, as I might create a more generic lower/intermediate
+ *	 (i.e. rmap command generation and transaction control) library,
+ *	 since the methods used here and for PLATO are IMO the smartest way
+ *	 to approach remote instrument RMAP control. This does however require
+ *	 a specific call interface, where I don't want va_args, so I can't just
+ *	 add another parameter.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rmap.h>
+#include <smile_fee_cmd.h>
+#include <smile_fee_rmap.h>
+
+
+/**
+ * @brief generate a read command for an arbitrary register (internal)
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the register address
+ *
+ * @note this will configure a multi-address, 4 byte wide read command, because
+ *	 the IWF RMAP core does not support single address read commands
+ */
+
+static int fee_read_cmd_register_internal(uint16_t trans_id, uint8_t *cmd,
+					   uint32_t addr)
+{
+	return smile_fee_gen_cmd(trans_id, cmd, RMAP_READ_ADDR_INC, addr, 4);
+}
+
+
+/**
+ * @brief generate a write command for an arbitrary register (internal)
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the register address
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a multi-address, 4 byte wide write command, because
+ *	 the IWF RMAP core does not support single address write commands with
+ *	 reply and CRC check enabled
+ */
+
+static int fee_write_cmd_register_internal(uint16_t trans_id, uint8_t *cmd,
+					    uint32_t addr)
+{
+	return smile_fee_gen_cmd(trans_id, cmd, RMAP_WRITE_ADDR_INC_VERIFY_REPLY,
+			    addr, 4);
+}
+
+
+/**
+ * @brief create a command to write arbitrary data to the RDCU (internal)
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the address to write to
+ * @param size the number of bytes to write
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a multi-address write command with reply enabled
+ */
+
+static int fee_write_cmd_data_internal(uint16_t trans_id, uint8_t *cmd,
+					uint32_t addr, uint32_t size)
+{
+	return smile_fee_gen_cmd(trans_id, cmd, RMAP_WRITE_ADDR_INC_REPLY,
+			    addr, size);
+}
+
+
+/**
+ * @brief create a command to read arbitrary data to the RDCU (internal)
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the address to read from
+ * @param size the number of bytes to read
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a multi-address read command
+ *
+ */
+
+static int fee_read_cmd_data_internal(uint16_t trans_id, uint8_t *cmd,
+				       uint32_t addr, uint32_t size)
+{
+	return smile_fee_gen_cmd(trans_id, cmd,
+			    RMAP_READ_ADDR_INC, addr, size);
+}
+
+
+/**
+ * @brief create a command to write arbitrary data to the RDCU
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the address to write to
+ * @param size the number of bytes to write
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a multi-address write command with reply enabled
+ */
+
+int fee_write_cmd_data(uint16_t trans_id, uint8_t *cmd,
+			uint32_t addr, uint32_t size)
+{
+	return fee_write_cmd_data_internal(trans_id, cmd, addr, size);
+}
+
+
+/**
+ *
+ * @brief create a command to read arbitrary data to the RDCU
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the address to read from
+ * @param size the number of bytes to read
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a multi-address read command
+ *
+ */
+
+int fee_read_cmd_data(uint16_t trans_id, uint8_t *cmd,
+		       uint32_t addr, uint32_t size)
+{
+	return fee_read_cmd_data_internal(trans_id, cmd, addr, size);
+}
+
+
+
+
+/**
+ * @brief generate a read command for an arbitrary register
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the register address
+ *
+ * @note this will configure a single address, 4 byte wide read command
+ */
+
+int fee_read_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, addr);
+}
+
+
+/**
+ * @brief generate a write command for an arbitrary register
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param addr the register address
+ *
+ * @returns the size of the command data buffer or 0 on error
+ *
+ * @note this will configure a single address, 4 byte wide write command with
+ *	 reply and CRC check enabled
+ */
+
+int fee_write_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, addr);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 0
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_0(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_0);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 1
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_1(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_1);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 2
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_2(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_2);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 3
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_3(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_3);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 4
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_4(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_4);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 5
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_5(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_5);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 18
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_18(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_18);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 19
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_19(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_19);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 20
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_20(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_20);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 21
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_21(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_21);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 22
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_22(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_22);
+}
+
+
+/**
+ * @brief create a command to read the FEE configuration register 24
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_read_cmd_cfg_reg_24(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_read_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_24);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 0
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_0(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_0);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 1
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_1(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_1);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 2
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_2(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_2);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 3
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_3(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_3);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 4
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_4(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_4);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 5
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_5(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_5);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 18
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_18(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_18);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 19
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_19(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_19);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 20
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_20(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_20);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 21
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_21(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_21);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 22
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_22(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_22);
+}
+
+
+/**
+ * @brief create a command to write the FEE configuration register 24
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int fee_write_cmd_cfg_reg_24(uint16_t trans_id, uint8_t *cmd)
+{
+	return fee_write_cmd_register_internal(trans_id, cmd, FEE_CFG_REG_24);
+}
diff --git a/FEE/smile_fee_cmd.h b/FEE/smile_fee_cmd.h
new file mode 100644
index 0000000000000000000000000000000000000000..8485e3d1d14912403608c19183e26432fbc5c414
--- /dev/null
+++ b/FEE/smile_fee_cmd.h
@@ -0,0 +1,124 @@
+/**
+ * @file   smile_fee_cmd.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2020
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE interface
+ *
+ */
+
+#ifndef SMILE_FEE_CMD_H
+#define SMILE_FEE_CMD_H
+
+#include <stdint.h>
+
+/* FEE RW registers (SMILE-MSSL-PL-Register_map_v0.10_Draft) */
+
+#define FEE_CFG_REG_0		0x00000000UL
+#define FEE_CFG_REG_1		0x00000004UL
+#define FEE_CFG_REG_2		0x00000008UL
+#define FEE_CFG_REG_3		0x0000000CUL
+#define FEE_CFG_REG_4		0x00000010UL
+#define FEE_CFG_REG_5		0x00000014UL
+#define FEE_CFG_REG_18		0x00000048UL
+#define FEE_CFG_REG_19		0x0000004CUL
+#define FEE_CFG_REG_20		0x00000050UL
+#define FEE_CFG_REG_21		0x00000054UL
+#define FEE_CFG_REG_22		0x00000058UL
+#define FEE_CFG_REG_24		0x00000060UL
+
+
+/* FEE  RO registers (SMILE-MSSL-PL-Register_map_v0.10_Draft) */
+
+#define FEE_HK_REG_0		0x00000700
+#define FEE_HK_REG_1		0x00000704
+#define FEE_HK_REG_2		0x00000708
+#define FEE_HK_REG_3		0x0000070C
+#define FEE_HK_REG_4		0x00000710
+#define FEE_HK_REG_5		0x00000714
+#define FEE_HK_REG_6		0x00000718
+#define FEE_HK_REG_7		0x0000071C
+#define FEE_HK_REG_8		0x00000720
+#define FEE_HK_REG_9		0x00000724
+#define FEE_HK_REG_10		0x00000728
+#define FEE_HK_REG_11		0x0000072C
+#define FEE_HK_REG_12		0x00000730
+#define FEE_HK_REG_13		0x00000734
+#define FEE_HK_REG_14		0x00000738
+#define FEE_HK_REG_15		0x0000073C
+#define FEE_HK_REG_16		0x00000740
+#define FEE_HK_REG_17		0x00000744
+#define FEE_HK_REG_18		0x00000748
+#define FEE_HK_REG_19		0x0000074C
+#define FEE_HK_REG_20		0x00000750
+#define FEE_HK_REG_21		0x00000754
+#define FEE_HK_REG_22		0x00000758
+#define FEE_HK_REG_23		0x0000075C
+#define FEE_HK_REG_24		0x00000760
+#define FEE_HK_REG_25		0x00000764
+#define FEE_HK_REG_26		0x00000768
+#define FEE_HK_REG_27		0x0000076C
+#define FEE_HK_REG_28		0x00000770
+#define FEE_HK_REG_29		0x00000774
+#define FEE_HK_REG_30		0x00000778
+#define FEE_HK_REG_31		0x0000077C
+#define FEE_HK_REG_32		0x00000780
+#define FEE_HK_REG_33		0x00000784
+#define FEE_HK_REG_34		0x00000788
+#define FEE_HK_REG_35		0x0000078C
+
+
+
+int fee_read_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr);
+int fee_write_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr);
+
+int fee_write_cmd_data(uint16_t trans_id, uint8_t *cmd,
+		       uint32_t addr, uint32_t size);
+int fee_read_cmd_data(uint16_t trans_id, uint8_t *cmd,
+		      uint32_t addr, uint32_t size);
+
+
+
+/* FEE read accessors */
+int fee_read_cmd_cfg_reg_0(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_1(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_2(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_3(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_4(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_5(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_18(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_19(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_20(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_21(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_22(uint16_t trans_id, uint8_t *cmd);
+int fee_read_cmd_cfg_reg_24(uint16_t trans_id, uint8_t *cmd);
+
+
+/* FEE write accessors */
+int fee_write_cmd_cfg_reg_0(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_1(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_2(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_3(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_4(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_5(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_18(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_19(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_20(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_21(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_22(uint16_t trans_id, uint8_t *cmd);
+int fee_write_cmd_cfg_reg_24(uint16_t trans_id, uint8_t *cmd);
+
+
+
+
+#endif /* SMILE_FEE_CMD_H */
diff --git a/FEE/smile_fee_ctrl.c b/FEE/smile_fee_ctrl.c
new file mode 100644
index 0000000000000000000000000000000000000000..274e85a81f4ba491f31e9042d708d12321421f7c
--- /dev/null
+++ b/FEE/smile_fee_ctrl.c
@@ -0,0 +1,1879 @@
+/**
+ * @file   smile_fee_ctrl.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief RMAP SMILE FEE control library
+ * @see SMILE-MSSL-PL-Register_map_v0.10_Draft
+ *
+ * USAGE:
+ *
+ *	Access to the local mirror is provided by _get or _set calls, so
+ *	to configure a register in the SMILE_FEE, one would call the sequence:
+ *
+ *	set_register_xyz(someargument);
+ *	sync_register_xyz_to_fee();
+ *
+ *	while(smile_fee_sync_status())
+ *		wait_busy_or_do_something_else_your_choice();
+ *
+ *	[sync_comple]
+ *
+ *	and to read a register:
+ *
+ *	sync_register_uvw_to_dpu()
+ *
+ *	while(smile_fee_sync_status())
+ *		wait_busy_or_do_something_else_your_choice();
+ *
+ *	[sync_comple]
+ *
+ *	value = get_register_uvw();
+ *
+ *
+ * WARNING: This has not been thoroughly tested and is in need of inspection
+ *	    with regards to the specification, in order to locate any
+ *	    register transcription errors or typos.
+ *	    Note that the FEE register layout may have been changed during the
+ *	    development process. Make sure to inspect the latest register
+ *	    map for changes.
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rmap.h>
+#include <smile_fee_cmd.h>
+#include <smile_fee_ctrl.h>
+#include <smile_fee_rmap.h>
+
+
+static struct smile_fee_mirror *smile_fee;
+
+
+
+/**
+ * @brief get the start of vertical row shared with charge injection
+ */
+
+uint16_t smile_fee_get_vstart(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_0 & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the start of vertical row shared with charge injection
+ */
+
+void smile_fee_set_vstart(uint16_t vstart)
+{
+	smile_fee->cfg_reg_0 &= ~0xFFFFUL;
+	smile_fee->cfg_reg_0 |=  0xFFFFUL & ((uint32_t) vstart);
+}
+
+/**
+ * @brief get the end of vertical row with charge injection
+ */
+
+uint16_t smile_fee_get_vend(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_0 >> 16) & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the end of vertical row with charge injection
+ */
+
+void smile_fee_set_vend(uint16_t vend)
+{
+	smile_fee->cfg_reg_0 &= ~(0xFFFFUL << 16);
+	smile_fee->cfg_reg_0 |=  (0xFFFFUL & ((uint32_t) vend)) << 16;
+}
+
+
+/**
+ * @brief get the charge injection width
+ */
+
+uint16_t smile_fee_get_charge_injection_width(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_1 & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the charge injection width
+ */
+
+void smile_fee_set_charge_injection_width(uint16_t width)
+{
+	smile_fee->cfg_reg_1 &= ~0xFFFFUL;
+	smile_fee->cfg_reg_1 |=  0xFFFFUL & ((uint32_t) width);
+}
+
+
+/**
+ * @brief get the charge injection gap
+ */
+
+uint16_t smile_fee_get_charge_injection_gap(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_1 >> 16) & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the charge injection gap
+ */
+
+void smile_fee_set_charge_injection_gap(uint16_t gap)
+{
+	smile_fee->cfg_reg_1 &= ~(0xFFFFUL << 16);
+	smile_fee->cfg_reg_1 |=  (0xFFFFUL & ((uint32_t) gap)) << 16;
+}
+
+/**
+ * @brief get the duration of a parallel overlap period (TOI)
+ */
+
+uint16_t smile_fee_get_parallel_toi_period(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_2 & 0xFFFUL);
+}
+
+
+/**
+ * @brief set the duration of a parallel overlap period (TOI)
+ */
+
+void smile_fee_set_parallel_toi_period(uint16_t period)
+{
+	smile_fee->cfg_reg_2 &= ~0xFFFUL;
+	smile_fee->cfg_reg_2 |=  0xFFFUL & ((uint32_t) period);
+}
+
+
+/**
+ * @brief get the extra parallel clock overlap
+ */
+
+uint16_t smile_fee_get_parallel_clk_overlap(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_2 >> 12) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set the extra parallel clock overlap
+ */
+
+void smile_fee_set_parallel_clk_overlap(uint16_t overlap)
+{
+	smile_fee->cfg_reg_2 &= ~(0xFFFUL << 12);
+	smile_fee->cfg_reg_2 |=  (0xFFFUL & ((uint32_t) overlap)) << 12;
+}
+
+
+/**
+ * @brief get CCD read-out
+ *
+ * @param ccd_id the id of the CCD (1 == CCD1 , 2 == CCD2, all others reserved)
+ *
+ * @note 1 indicates that CCD is read-out
+ *
+ * @returns 1 if ccd is read-out, 0 otherwise; always returns 0 if reserved
+ *	    CCD ID is issued
+ */
+
+uint32_t smile_fee_get_ccd_readout(uint32_t ccd_id)
+{
+	if (!ccd_id)
+		return 0;
+
+	if (ccd_id > 2)
+		return 0;
+
+	/* note: bit index starts at 0, but ccd id starts at 1, hence the
+	 * subtraction
+	 */
+	return (smile_fee->cfg_reg_2 >> 24) & (1 << (ccd_id - 1));
+}
+
+
+/**
+ * @brief set CCD read-out
+ *
+ * @param ccd_id the id of the CCD (1 == CCD1 , 2 == CCD2, all others reserved)
+ * @param status the read-out status to set, either 0 or any bit set
+ *
+ * @note 1 indicates that CCD is read-out
+ *
+ */
+
+void smile_fee_set_ccd_readout(uint32_t ccd_id, uint32_t status)
+{
+	if (!ccd_id)
+		return;
+
+	if (ccd_id > 2)
+		return;
+
+	if (status)
+		status = 1;
+
+	/* note: bit index starts at 0, but ccd id starts at 1, hence the
+	 * subtraction
+	 */
+
+	smile_fee->cfg_reg_2 &= ~((status << (ccd_id - 1)) << 24);
+	smile_fee->cfg_reg_2 |=  ((status << (ccd_id - 1)) << 24);
+}
+
+
+/**
+ * @brief get the amount of lines to be dumped after readout
+ */
+
+uint16_t smile_fee_get_n_final_dump(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_3 & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the amount of lines to be dumped after readout
+ */
+
+void smile_fee_set_n_final_dump(uint16_t lines)
+{
+	smile_fee->cfg_reg_3 &= ~0xFFFFUL;
+	smile_fee->cfg_reg_3 |=  0xFFFFUL & ((uint32_t) lines);
+}
+
+
+/**
+ * @brief get the amount of serial register transfers
+ */
+
+uint16_t smile_fee_get_h_end(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_3 >> 16) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set the amount of serial register transfers
+ */
+
+void smile_fee_set_h_end(uint16_t transfers)
+{
+	smile_fee->cfg_reg_2 &= ~(0xFFFUL << 16);
+	smile_fee->cfg_reg_2 |=  (0xFFFUL & ((uint32_t) transfers)) << 16;
+}
+
+
+/**
+ * @brief get charge injection mode
+ *
+ * @returns 0 if disabled, bits set otherwise
+ */
+
+uint32_t smile_fee_get_charge_injection(void)
+{
+	return (smile_fee->cfg_reg_3 >> 28) & 0x1UL;
+}
+
+
+/**
+ * @brief set the charge injection mode
+ *
+ * @param mode enable/disable injection; either 0 to disable or any bit set to
+ *	       enable
+ */
+
+void smile_fee_set_charge_injection(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_3 &= ~(0x1UL << 28);
+	smile_fee->cfg_reg_3 |=  (mode << 28);
+}
+
+
+/**
+ * @brief get parallel clock generation
+ *
+ * @returns 1 if bi-levels parallel clocks are generated, 0 if tri-level
+ *	    parallel clocks are generated
+ */
+
+uint32_t smile_fee_get_tri_level_clk(void)
+{
+	return (smile_fee->cfg_reg_3 >> 29) & 0x1UL;
+}
+
+
+/**
+ * @brief set parallel clock generation
+ *
+ * @param mode set 0 for tri-level parallel clock generation,
+ *	       any bit set for bi-level parallel clock generation
+ */
+
+void smile_fee_set_tri_level_clk(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_3 &= ~(0x1UL << 29);
+	smile_fee->cfg_reg_3 |=  (mode  << 29);
+}
+
+
+/**
+ * @brief get image clock direction
+ *
+ * @returns 1 if reverse parallel clocks are generated, 0 if normal forward
+ *	    parallel clocks are generated
+ */
+
+uint32_t smile_fee_get_img_clk_dir(void)
+{
+	return (smile_fee->cfg_reg_3 >> 30) & 0x1UL;
+}
+
+
+/**
+ * @brief set image clock direction
+
+ *
+ * @param mode set 0 for normal forward parallel clock generation
+ *	       any bit set for reverse parallel clock generation
+ */
+
+void smile_fee_set_img_clk_dir(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_3 &= ~(0x1UL << 30);
+	smile_fee->cfg_reg_3 |=  (mode  << 30);
+}
+
+
+/**
+ * @brief get serial clock direction
+ *
+ * @returns 1 if reverse serial clocks are generated, 0 if normal forward
+ *	    serial clocks are generated
+ */
+
+uint32_t smile_fee_get_clk_dir(void)
+{
+	return (smile_fee->cfg_reg_3 >> 31) & 0x1UL;
+}
+
+
+/**
+ * @brief set serial clock direction
+ *
+ * @param mode set 0 for normal forward serial clock generation
+ *	       any bit set for reverse serial clock generation
+ */
+
+void smile_fee_set_clk_dir(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_3 &= ~(0x1UL << 31);
+	smile_fee->cfg_reg_3 |=  (mode  << 31);
+}
+
+
+/**
+ * @brief get packet size
+ *
+ * @returns 10 byte packed header + number of load bytes; number of load bytes
+ *	    are multiple of 4.
+ */
+
+uint16_t smile_fee_get_packet_size(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_4 & 0xFFFFUL);
+}
+
+/**
+ * @brief set packet size
+ *
+ * @param pkt_size 10 byte packed header + number of load bytes; number of load
+ *		   bytes must be a multiple of 4
+ */
+
+void smile_fee_set_packet_size(uint16_t pkt_size)
+{
+	smile_fee->cfg_reg_4 &= ~0xFFFFUL;
+	smile_fee->cfg_reg_4 |=  0xFFFFUL & ((uint32_t) pkt_size);
+}
+
+
+/**
+ * @brief get the integration period
+ */
+
+uint16_t smile_fee_get_int_period(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_4 >> 16) & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set the integration period
+ */
+
+void smile_fee_set_int_period(uint16_t period)
+{
+	smile_fee->cfg_reg_4 &= ~(0xFFFFUL << 16);
+	smile_fee->cfg_reg_4 |=  (0xFFFFUL & ((uint32_t) period)) << 16;
+}
+
+
+/**
+ * @brief get internal mode sync
+ *
+ * @returns 1 if internal mode sync is enabvle, 0 if disabled
+ */
+
+uint32_t smile_fee_get_sync_sel(void)
+{
+	return (smile_fee->cfg_reg_5 >> 20) & 0x1UL;
+}
+
+
+/**
+ * @brief set internal mode sync
+ *
+ * @param mode set 0 to disable internal mode sync
+ *	       any bit set to enable internal mode sync
+ */
+
+void smile_fee_set_sync_sel(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_5 &= ~(0x1UL << 20);
+	smile_fee->cfg_reg_5 |=  (mode  << 20);
+}
+
+
+/**
+ * @brief get the readout node(s)
+ *
+ * @returns 0x1 if read-out via F-side
+ *          0x2 if read-out via E-side
+ *          0x3 if read-out via both F- and E-side
+ */
+
+uint16_t smile_fee_get_readout_nodes(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_5 >> 21) & 0x3UL);
+}
+
+
+/**
+ * @brief set the readout node(s)
+ *
+ * @param nodes set 0x1 for read-out via F-side,
+ *		    0x2 if read-out via E-side,
+ *		    0x3 if read-out via both F- and E-side
+ */
+
+void smile_fee_set_readout_nodes(uint32_t nodes)
+{
+	if (!nodes)
+		return;
+
+	if (nodes > 3)
+		return;
+
+	smile_fee->cfg_reg_5 &= ~(0x3UL << 21);
+	smile_fee->cfg_reg_5 |=  (0x3UL & nodes) << 21;
+}
+
+
+/**
+ * @brief get digitise mode
+ *
+ * @returns 1 if digitise data is transferred to DPU during an image mode,
+ *	      else image mode is executed but not pixel data is transferred
+ */
+
+uint32_t smile_fee_get_digitise(void)
+{
+	return (smile_fee->cfg_reg_5 >> 23) & 0x1UL;
+}
+
+
+/**
+ * @brief set digitise mode
+ *
+ * @param mode set 0 to disable pixel data transfer to DPU
+ *	       any bit set to enable pixel data transfer
+ */
+
+void smile_fee_set_digitise(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_5 &= ~(0x1UL << 23);
+	smile_fee->cfg_reg_5 |=  (mode  << 23);
+}
+
+
+/**
+ * @brief get correction bypass
+ *
+ * @returns 1 if no correction is applied,
+ *	    else background correction is applied to the read-out pixel data
+ */
+
+uint32_t smile_fee_get_correction_bypass(void)
+{
+	return (smile_fee->cfg_reg_5 >> 24) & 0x1UL;
+}
+
+
+/**
+ * @brief set correction bypass
+ *
+ * @param mode set 0 to enable pixel data background correction
+ *	       any bit set to disable background correction
+ */
+
+void smile_fee_set_correction_bypass(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_5 &= ~(0x1UL << 24);
+	smile_fee->cfg_reg_5 |=  (mode  << 24);
+}
+
+
+/**
+ * @brief get vod_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_vod_config(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_18 & 0xFFFUL);
+}
+
+
+/**
+ * @brief set vod_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_vod_config(uint16_t vod)
+{
+	smile_fee->cfg_reg_18 &= ~0xFFFUL;
+	smile_fee->cfg_reg_18 |=  0xFFFUL & ((uint32_t) vod);
+}
+
+
+/**
+ * @brief get ccd1_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd1_vrd_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_18 >> 12) & 0xFFFUL);
+}
+
+/**
+ * @brief set ccd1_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd1_vrd_config(uint16_t vrd)
+{
+	smile_fee->cfg_reg_18 &= ~(0xFFFUL << 12);
+	smile_fee->cfg_reg_18 |=  (0xFFFUL & ((uint32_t) vrd)) << 12;
+}
+
+
+/**
+ * @brief get ccd2_vrd_config
+ *
+ * @note no description available in register map document
+ *
+ * @warn this is a case of extreme idiocy from a user's perspective.
+ *	 make sure to sync both reg 18 and 19 to DPU before using this function
+ */
+
+uint16_t smile_fee_get_ccd2_vrd_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_18 >> 24) & 0xFFUL)
+			| ((smile_fee->cfg_reg_19 & 0x3UL) << 8);
+}
+
+/**
+ * @brief set ccd2_vrd_config
+ *
+ * @note no description available in register map document
+ *
+ * @warn see smile_fee_get_ccd2_vrd_config; make sure to sync both reg 18 and 19
+ *	 to FEE after using this function
+ */
+
+void smile_fee_set_ccd2_vrd_config(uint16_t vrd)
+{
+	smile_fee->cfg_reg_18 &= ~(0xFFUL << 24);
+	smile_fee->cfg_reg_18 |=  (0xFFUL & ((uint32_t) vrd)) << 24;
+
+	smile_fee->cfg_reg_19 &= ~0x3UL;
+	smile_fee->cfg_reg_19 |=  0x3UL & (((uint32_t) vrd) >> 8);
+}
+
+
+/**
+ * @brief get ccd3_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd3_vrd_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_19 >> 4) & 0xFFFUL);
+}
+
+/**
+ * @brief set ccd3_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd3_vrd_config(uint16_t vrd)
+{
+	smile_fee->cfg_reg_19 &= ~(0xFFFUL << 4);
+	smile_fee->cfg_reg_19 |=  (0xFFFUL & ((uint32_t) vrd)) << 4;
+}
+
+
+/**
+ * @brief get ccd4_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd4_vrd_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_19 >> 16) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set ccd4_vrd_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd4_vrd_config(uint16_t vrd)
+{
+	smile_fee->cfg_reg_19 &= ~(0xFFFUL << 16);
+	smile_fee->cfg_reg_19 |=  (0xFFFUL & ((uint32_t) vrd)) << 16;
+}
+
+
+/**
+ * @brief get ccd_vgd_config
+ *
+ * @note no description available in register map document
+ *
+ * @warn great, another word boundary overlap...
+ *	 make sure to sync both reg 19 and 20 to DPU before using this function
+ */
+
+uint16_t smile_fee_get_ccd_vgd_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_19 >> 28) & 0x3UL)
+			| ((smile_fee->cfg_reg_20 & 0xFFUL) << 3);
+}
+
+/**
+ * @brief set ccd_vgd_config
+ *
+ * @note no description available in register map document
+ *
+ * @warn see smile_fee_get_ccd_vgd_config; make sure to sync both reg 19 and 20
+ *	 to FEE after using this function
+ */
+
+void smile_fee_set_ccd_vgd_config(uint16_t vgd)
+{
+	smile_fee->cfg_reg_19 &= ~(0x3UL << 28);
+	smile_fee->cfg_reg_19 |=  (0x3UL & ((uint32_t) vgd)) << 28;
+
+	smile_fee->cfg_reg_20 &= ~0xFFUL;
+	smile_fee->cfg_reg_20 |=  0xFFUL & (((uint32_t) vgd) >> 3);
+}
+
+
+/**
+ * @brief get ccd_vog_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd_vog_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_20 >> 8) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set ccd_vog_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd_vog_config(uint16_t vog)
+{
+	smile_fee->cfg_reg_20 &= ~(0xFFFUL << 8);
+	smile_fee->cfg_reg_20 |=  (0xFFFUL & ((uint32_t) vog)) << 8;
+}
+
+
+/**
+ * @brief get ccd_ig_hi_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd_ig_hi_config(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_20 >> 20) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set ccd_ig_hi_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd_ig_hi_config(uint16_t cfg)
+{
+	smile_fee->cfg_reg_20 &= ~(0xFFFUL << 20);
+	smile_fee->cfg_reg_20 |=  (0xFFFUL & ((uint32_t) cfg)) << 20;
+}
+
+
+/**
+ * @brief get ccd_ig_lo_config
+ *
+ * @note no description available in register map document
+ */
+
+uint16_t smile_fee_get_ccd_ig_lo_config(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_21 & 0xFFFUL);
+}
+
+
+/**
+ * @brief set ccd_ig_lo_config
+ *
+ * @note no description available in register map document
+ */
+
+void smile_fee_set_ccd_ig_lo_config(uint16_t cfg)
+{
+	smile_fee->cfg_reg_21 &= ~0xFFFUL;
+	smile_fee->cfg_reg_21 |=  0xFFFUL & (uint32_t) cfg;
+}
+
+
+/**
+ * @brief get start row number
+ */
+
+uint16_t smile_fee_get_h_start(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_21 >> 12) & 0xFFFUL);
+}
+
+
+/**
+ * @brief set start row number
+ */
+
+void smile_fee_set_h_start(uint16_t row)
+{
+	smile_fee->cfg_reg_21 &= ~(0xFFFUL << 12);
+	smile_fee->cfg_reg_21 |=  (0xFFFUL & ((uint32_t) row)) << 12;
+}
+
+
+/**
+ * @brief get next mode of operation
+ *
+ * @note as per register map document, the following return values
+ *	 are currently valid:
+ *	 0x0	on mode
+ *	 0x1	full frame pattern mode
+ *	 0x4	stand-by mode
+ *	 0xd	immediate on mode (this is a command, not a mode)
+ *	 0xf	frame transfer mode
+ *	 0xe	full frame mode
+ */
+
+uint8_t smile_fee_get_ccd_mode_config(void)
+{
+	return (uint8_t) ((smile_fee->cfg_reg_21 >> 24) & 0xFUL);
+}
+
+
+/**
+ * @brief set next mode of operation
+ *
+ * @param mode the mode to set
+ *
+ * @note as per register map document, the following return values
+ *	 are currently valid:
+ *	 0x0	on mode
+ *	 0x1	full frame pattern mode
+ *	 0x4	stand-by mode
+ *	 0xd	immediate on mode (this is a command, not a mode)
+ *	 0xf	frame transfer mode
+ *	 0xe	full frame mode
+ *
+ * @warn input parameter is not checked for validity
+ */
+
+void smile_fee_set_ccd_mode_config(uint8_t mode)
+{
+	smile_fee->cfg_reg_21 &= ~(0xFUL << 24);
+	smile_fee->cfg_reg_21 |=  (0xFUL & ((uint32_t) mode)) << 24;
+}
+
+
+/**
+ * @brief get degree of binning
+ *
+ *
+ * @returns 0x1 if no binning
+ *	    0x2 if 6x6 binning
+ *	    0x3 if 24x24 binning
+ */
+
+uint8_t smile_fee_get_ccd_mode2_config(void)
+{
+	return (uint8_t) ((smile_fee->cfg_reg_21 >> 28) & 0x3UL);
+}
+
+
+/**
+ * @brief set degree of binning
+ *
+ * @param mode the mode to set:
+ *	       0x1 :  no binning
+ *             0x2 : 6x6 binning
+ *             0x3 : 24x24 binning
+ */
+
+void smile_fee_set_ccd_mode2_config(uint8_t mode)
+{
+	smile_fee->cfg_reg_21 &= ~(0x3UL << 28);
+	smile_fee->cfg_reg_21 |=  (0x3UL & ((uint32_t) mode)) << 28;
+}
+
+
+/**
+ * @brief get event detection execution
+ *
+ * @returns 1 if event detection is executed, 0 otherwise
+ */
+
+uint32_t smile_fee_get_event_detection(void)
+{
+	return (smile_fee->cfg_reg_21 >> 30) & 0x1UL;
+}
+
+
+/**
+ * @brief set event detection execution
+ *
+ * @param mode set 0 to disable event detection
+ *	       any bit set to enable event detection
+ */
+
+void smile_fee_set_event_detection(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_21 &= ~(0x1UL << 30);
+	smile_fee->cfg_reg_21 |=  (mode  << 30);
+}
+
+
+/**
+ * @brief get error flags clear
+ *
+ * @returns 1 if flag is set, 0 otherwise
+ *
+ */
+
+uint32_t smile_fee_get_clear_error_flag(void)
+{
+	return (smile_fee->cfg_reg_21 >> 31) & 0x1UL;
+}
+
+
+/**
+ * @brief set error flags clear
+ *
+ * @param mode set 0 to disable clear flags
+ *	       any bit set to enable clear error flags
+ *
+ *
+ * @warn when set to 1, all error flags generated by the FEE FPGA
+ *	 for non-RMAP/SPW related functions are immediately cleared.
+ *	 The bit is the reset by the FPGA to re-enable error flags
+ *	 BE WARNED: if you set this flag to 1 locally, the FEE will clear all
+ *	 flags everytime you sync this register from DPU -> FEE as long
+ *	 as the bit is set in the local FEE mirror
+ */
+
+void smile_fee_set_clear_error_flag(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_21 &= ~(0x1UL << 31);
+	smile_fee->cfg_reg_21 |=  (mode  << 31);
+}
+
+
+/**
+ * @brief get ccd1 single pixel threshold
+ *
+ * @note this is the threshold above which a pixel may be considered a possible
+ *	 soft X-ray event
+ */
+
+uint16_t smile_fee_get_ccd1_single_pixel_treshold(void)
+{
+	return (uint16_t) (smile_fee->cfg_reg_22 & 0xFFFUL);
+}
+
+
+/**
+ * @brief set ccd1 single pixel threshold
+ *
+ * @param threshold the threshold above which a pixel may be considered
+ *		    a possible soft X-ray event
+ */
+
+void smile_fee_set_ccd1_single_pixel_treshold(uint16_t threshold)
+{
+	smile_fee->cfg_reg_22 &= ~0xFFFUL;
+	smile_fee->cfg_reg_22 |=  0xFFFUL & ((uint32_t) threshold);
+}
+
+
+/**
+ * @brief get ccd2 single pixel threshold
+ *
+ * @note this is the threshold above which a pixel may be considered a possible
+ *	 soft X-ray event
+ */
+
+uint16_t smile_fee_get_ccd2_single_pixel_treshold(void)
+{
+	return (uint16_t) ((smile_fee->cfg_reg_22 >> 16) & 0xFFFFUL);
+}
+
+
+/**
+ * @brief set ccd2 single pixel threshold
+ *
+ * @param threshold the threshold above which a pixel may be considered
+ *		    a possible soft X-ray event
+ */
+
+void smile_fee_set_ccd2_single_pixel_treshold(uint16_t threshold)
+{
+	smile_fee->cfg_reg_22 &= ~(0xFFFFUL << 16);
+	smile_fee->cfg_reg_22 |=  (0xFFFFUL & ((uint32_t) threshold)) << 16;
+}
+
+
+/**
+ * @brief get execute op flag
+ *
+ * @returns 1 if execute op flag is set, 0 otherwise
+ *
+ * @note if this flag is set, the set operational parameters are expedited
+ * @warn the register map document does not detail any behaviour, i.e. whether
+ *	 this flag clears itself or must be cleared by the user
+ *
+ */
+
+uint32_t smile_fee_get_execute_op(void)
+{
+	return smile_fee->cfg_reg_24 & 0x1UL;
+}
+
+
+/**
+ * @brief set execute op flag
+ *
+ * @param mode set 0 to disable execute op
+ *	       any bit set to enable execute op
+ *
+ * @note if this flag is set, the set operational parameters are expedited
+ * @warn the register map document does not detail any behaviour, i.e. whether
+ *	 this flag clears itself or must be cleared by the user
+ *	 ASSUME YOU HAVE TO CLEAR THIS EXPLICITLY BEFORE CHANGING PARAMETERS OR
+ *	 EXECUTING A DPU->FEE SYNC
+ */
+
+void smile_fee_set_execute_op(uint32_t mode)
+{
+	if (mode)
+		mode = 1;
+
+
+	smile_fee->cfg_reg_24 &= ~0x1UL;
+	smile_fee->cfg_reg_24 |= mode;
+}
+
+
+/**
+ * @brief sync configuration register 0
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_0(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_0,
+				      &smile_fee->cfg_reg_0, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_0,
+				      &smile_fee->cfg_reg_0, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 1
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_1(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_1,
+				      &smile_fee->cfg_reg_1, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_1,
+				      &smile_fee->cfg_reg_1, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 2
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_2(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_2,
+				      &smile_fee->cfg_reg_2, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_2,
+				      &smile_fee->cfg_reg_2, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 3
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_3(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_3,
+				      &smile_fee->cfg_reg_3, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_3,
+				      &smile_fee->cfg_reg_3, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 4
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_4(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_4,
+				      &smile_fee->cfg_reg_4, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_4,
+				      &smile_fee->cfg_reg_4, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 5
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_5(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_5,
+				      &smile_fee->cfg_reg_5, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_5,
+				      &smile_fee->cfg_reg_5, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 18
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_18(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_18,
+				      &smile_fee->cfg_reg_18, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_18,
+				      &smile_fee->cfg_reg_18, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 19
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_19(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_19,
+				      &smile_fee->cfg_reg_19, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_19,
+				      &smile_fee->cfg_reg_19, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 20
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_20(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_20,
+				      &smile_fee->cfg_reg_20, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_20,
+				      &smile_fee->cfg_reg_20, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 21
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_21(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_21,
+				      &smile_fee->cfg_reg_21, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_21,
+				      &smile_fee->cfg_reg_21, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 22
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_22(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_22,
+				      &smile_fee->cfg_reg_22, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_22,
+				      &smile_fee->cfg_reg_22, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync configuration register 24
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+int smile_fee_sync_cfg_reg_24(enum sync_direction dir)
+{
+	if (dir == FEE2DPU)
+		return smile_fee_sync(fee_read_cmd_cfg_reg_24,
+				      &smile_fee->cfg_reg_24, 0);
+
+	if (dir == DPU2FEE)
+		return smile_fee_sync(fee_write_cmd_cfg_reg_24,
+				      &smile_fee->cfg_reg_24, 4);
+
+	return -1;
+}
+
+
+/**
+ * @brief sync register containing vstart
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_vstart(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_0(dir);
+}
+
+
+/**
+ * @brief sync register containing vend
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_vend(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_0(dir);
+}
+
+
+/**
+ * @brief sync register containing charge injection width
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_charge_injection_width(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_1(dir);
+}
+
+/**
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_charge_injection_gap(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_1(dir);
+}
+
+
+/**
+ * @brief sync the duration of a parallel overlap period (TOI)
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_parallel_toi_period(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_2(dir);
+}
+
+/**
+ * @brief sync the extra parallel clock overlap
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_parallel_clk_overlap(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_2(dir);
+}
+
+
+/**
+ * @brief sync CCD read-out
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_readout(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_2(dir);
+}
+
+
+/**
+ * @brief sync the amount of lines to be dumped after readout
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_n_final_dump(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync the amount of serial register transfers
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_h_end(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync charge injection mode
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_charge_injection(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync parallel clock generation
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_tri_level_clk(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync image clock direction
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_img_clk_dir(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync serial clock direction
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_clk_dir(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_3(dir);
+}
+
+
+/**
+ * @brief sync packet size
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_packet_size(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_4(dir);
+}
+
+
+/**
+ * @brief sync the integration period
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_int_period(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_4(dir);
+}
+
+
+/**
+ * @brief sync internal mode sync
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_sync_sel(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_5(dir);
+}
+
+
+/**
+ * @brief sync the readout node(s)
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_readout_nodes(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_5(dir);
+}
+
+
+/**
+ * @brief sync digitise mode
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_digitise(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_5(dir);
+}
+
+
+/**
+ * @brief sync correction bypass
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_correction_bypass(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_5(dir);
+}
+
+
+/**
+ * @brief sync vod_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_vod_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_18(dir);
+}
+
+
+/**
+ * @brief sync ccd1_vrd_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd1_vrd_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_18(dir);
+}
+
+
+/**
+ * @brief sync ccd2_vrd_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd2_vrd_config(enum sync_direction dir)
+{
+	int ret;
+
+	ret = smile_fee_sync_cfg_reg_18(dir);
+	if (ret)
+		return ret;
+
+	return smile_fee_sync_cfg_reg_19(dir);
+}
+
+
+/**
+ * @brief sync ccd3_vrd_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd3_vrd_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_19(dir);
+}
+
+
+/**
+ * @brief sync ccd4_vrd_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd4_vrd_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_19(dir);
+}
+
+
+/**
+ * @brief sync ccd_vgd_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_vgd_config(enum sync_direction dir)
+{
+	int ret;
+
+	ret = smile_fee_sync_cfg_reg_19(dir);
+	if (ret)
+		return ret;
+
+	return smile_fee_sync_cfg_reg_20(dir);
+}
+
+
+/**
+ * @brief sync ccd_vog_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_vog_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_20(dir);
+}
+
+
+/**
+ * @brief get ccd_ig_hi_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_ig_hi_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_20(dir);
+}
+
+
+/**
+ * @brief sync ccd_ig_lo_config
+ * @note no description available in register map document
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_ig_lo_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync start row number
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_h_start(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync next mode of operation
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_mode_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync degree of binning
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd_mode2_config(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync event detection execution
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_event_detection(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync error flags clear
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_clear_error_flag(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_21(dir);
+}
+
+
+/**
+ * @brief sync ccd1 single pixel threshold
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd1_single_pixel_treshold(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_22(dir);
+}
+
+
+/**
+ * @brief sync ccd2 single pixel threshold
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_ccd2_single_pixel_treshold(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_22(dir);
+}
+
+
+/**
+ * @brief sync execute op flag
+ *
+ * @param dir the syncronisation direction
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_sync_execute_op(enum sync_direction dir)
+{
+	return smile_fee_sync_cfg_reg_24(dir);
+}
+
+
+
+
+
+
+
+
+
+/**
+ * @brief initialise the smile_fee control library
+ */
+
+void smile_fee_ctrl_init(void)
+{
+	smile_fee = (struct smile_fee_mirror *) malloc(sizeof(struct smile_fee_mirror));
+	if (!smile_fee)
+		printf("Error allocating memory for the SMILE_FEE mirror\n");
+
+	bzero(smile_fee, sizeof(struct smile_fee_mirror));
+}
diff --git a/FEE/smile_fee_ctrl.h b/FEE/smile_fee_ctrl.h
new file mode 100644
index 0000000000000000000000000000000000000000..a60dbe5b40724a88e5960bc181cb1494879c8f2a
--- /dev/null
+++ b/FEE/smile_fee_ctrl.h
@@ -0,0 +1,245 @@
+/**
+ * @file   smile_fee_ctrl.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2020
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief SMILE FEE control interface
+ */
+#ifndef _SMILE_FEE_CTRL_H_
+#define _SMILE_FEE_CTRL_H_
+
+#include <stdint.h>
+
+__extension__
+struct smile_fee_mirror {
+
+	/* FEE RW registers (SMILE-MSSL-PL-Register_map_v0.10_Draft) */
+	/* (includes unused) */
+	uint32_t cfg_reg_0;
+	uint32_t cfg_reg_1;
+	uint32_t cfg_reg_2;
+	uint32_t cfg_reg_3;
+	uint32_t cfg_reg_4;
+	uint32_t cfg_reg_5;
+	uint32_t cfg_reg_6;
+	uint32_t cfg_reg_7;
+	uint32_t cfg_reg_8;
+	uint32_t cfg_reg_9;
+	uint32_t cfg_reg_10;
+	uint32_t cfg_reg_11;
+	uint32_t cfg_reg_12;
+	uint32_t cfg_reg_13;
+	uint32_t cfg_reg_14;
+	uint32_t cfg_reg_15;
+	uint32_t cfg_reg_16;
+	uint32_t cfg_reg_17;
+	uint32_t cfg_reg_18;
+	uint32_t cfg_reg_19;
+	uint32_t cfg_reg_20;
+	uint32_t cfg_reg_21;
+	uint32_t cfg_reg_22;
+	uint32_t cfg_reg_23;
+	uint32_t cfg_reg_24;
+
+	/* FEE  RO registers (SMILE-MSSL-PL-Register_map_v0.10_Draft) */
+
+	uint32_t unused[1696];	/* TODO */
+
+	uint32_t hk_reg_0;
+	uint32_t hk_reg_1;
+	uint32_t hk_reg_2;
+	uint32_t hk_reg_3;
+	uint32_t hk_reg_4;
+	uint32_t hk_reg_5;
+	uint32_t hk_reg_6;
+	uint32_t hk_reg_7;
+	uint32_t hk_reg_8;
+	uint32_t hk_reg_9;
+	uint32_t hk_reg_10;
+	uint32_t hk_reg_11;
+	uint32_t hk_reg_12;
+	uint32_t hk_reg_13;
+	uint32_t hk_reg_14;
+	uint32_t hk_reg_15;
+	uint32_t hk_reg_16;
+	uint32_t hk_reg_17;
+	uint32_t hk_reg_18;
+	uint32_t hk_reg_19;
+	uint32_t hk_reg_20;
+	uint32_t hk_reg_21;
+	uint32_t hk_reg_22;
+	uint32_t hk_reg_23;
+	uint32_t hk_reg_24;
+	uint32_t hk_reg_25;
+	uint32_t hk_reg_26;
+	uint32_t hk_reg_27;
+	uint32_t hk_reg_28;
+	uint32_t hk_reg_29;
+	uint32_t hk_reg_30;
+	uint32_t hk_reg_31;
+	uint32_t hk_reg_32;
+	uint32_t hk_reg_33;
+	uint32_t hk_reg_34;
+	uint32_t hk_reg_35;
+
+	/* arbitaray ram area */
+	/* uint8_t *sram; */
+};
+
+/* register sync */
+
+/**
+ * @note FEE2DPU is for "read" commands
+ *	 DPU2FEE is for "write" commands
+ */
+enum sync_direction {FEE2DPU, DPU2FEE};
+
+/* whole registers */
+int smile_fee_sync_cfg_reg_0(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_1(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_2(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_3(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_4(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_5(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_18(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_19(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_20(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_21(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_22(enum sync_direction dir);
+int smile_fee_sync_cfg_reg_24(enum sync_direction dir);
+
+/* values contained in registers */
+int smile_fee_sync_vstart(enum sync_direction dir);
+int smile_fee_sync_vend(enum sync_direction dir);
+int smile_fee_sync_charge_injection_width(enum sync_direction dir);
+int smile_fee_sync_charge_injection_gap(enum sync_direction dir);
+int smile_fee_sync_parallel_toi_period(enum sync_direction dir);
+int smile_fee_sync_parallel_clk_overlap(enum sync_direction dir);
+int smile_fee_sync_ccd_readout(enum sync_direction dir);
+int smile_fee_sync_n_final_dump(enum sync_direction dir);
+int smile_fee_sync_h_end(enum sync_direction dir);
+int smile_fee_sync_charge_injection(enum sync_direction dir);
+int smile_fee_sync_tri_level_clk(enum sync_direction dir);
+int smile_fee_sync_img_clk_dir(enum sync_direction dir);
+int smile_fee_sync_clk_dir(enum sync_direction dir);
+int smile_fee_sync_packet_size(enum sync_direction dir);
+int smile_fee_sync_int_period(enum sync_direction dir);
+int smile_fee_sync_sync_sel(enum sync_direction dir);
+int smile_fee_sync_readout_nodes(enum sync_direction dir);
+int smile_fee_sync_digitise(enum sync_direction dir);
+int smile_fee_sync_correction_bypass(enum sync_direction dir);
+int smile_fee_sync_vod_config(enum sync_direction dir);
+int smile_fee_sync_ccd1_vrd_config(enum sync_direction dir);
+int smile_fee_sync_ccd2_vrd_config(enum sync_direction dir);
+int smile_fee_sync_ccd3_vrd_config(enum sync_direction dir);
+int smile_fee_sync_ccd4_vrd_config(enum sync_direction dir);
+int smile_fee_sync_ccd_vgd_config(enum sync_direction dir);
+int smile_fee_sync_ccd_vog_config(enum sync_direction dir);
+int smile_fee_sync_ccd_ig_hi_config(enum sync_direction dir);
+int smile_fee_sync_ccd_ig_lo_config(enum sync_direction dir);
+int smile_fee_sync_h_start(enum sync_direction dir);
+int smile_fee_sync_ccd_mode_config(enum sync_direction dir);
+int smile_fee_sync_ccd_mode2_config(enum sync_direction dir);
+int smile_fee_sync_event_detection(enum sync_direction dir);
+int smile_fee_sync_clear_error_flag(enum sync_direction dir);
+int smile_fee_sync_ccd1_single_pixel_treshold(enum sync_direction dir);
+int smile_fee_sync_ccd2_single_pixel_treshold(enum sync_direction dir);
+int smile_fee_sync_execute_op(enum sync_direction dir);
+
+
+
+/* read SMILE_FEE register mirror */
+uint16_t smile_fee_get_vstart(void);
+uint16_t smile_fee_get_vend(void);
+uint16_t smile_fee_get_charge_injection_width(void);
+uint16_t smile_fee_get_charge_injection_gap(void);
+uint16_t smile_fee_get_parallel_toi_period(void);
+uint16_t smile_fee_get_parallel_clk_overlap(void);
+uint32_t smile_fee_get_ccd_readout(uint32_t ccd_id);
+uint16_t smile_fee_get_n_final_dump(void);
+uint16_t smile_fee_get_h_end(void);
+uint32_t smile_fee_get_charge_injection(void);
+uint32_t smile_fee_get_tri_level_clk(void);
+uint32_t smile_fee_get_img_clk_dir(void);
+uint32_t smile_fee_get_clk_dir(void);
+uint16_t smile_fee_get_packet_size(void);
+uint16_t smile_fee_get_int_period(void);
+uint32_t smile_fee_get_sync_sel(void);
+uint16_t smile_fee_get_readout_nodes(void);
+uint32_t smile_fee_get_digitise(void);
+uint32_t smile_fee_get_correction_bypass(void);
+uint16_t smile_fee_get_vod_config(void);
+uint16_t smile_fee_get_ccd1_vrd_config(void);
+uint16_t smile_fee_get_ccd2_vrd_config(void);
+uint16_t smile_fee_get_ccd3_vrd_config(void);
+uint16_t smile_fee_get_ccd4_vrd_config(void);
+uint16_t smile_fee_get_ccd_vgd_config(void);
+uint16_t smile_fee_get_ccd_vog_config(void);
+uint16_t smile_fee_get_ccd_ig_hi_config(void);
+uint16_t smile_fee_get_ccd_ig_lo_config(void);
+uint16_t smile_fee_get_h_start(void);
+uint8_t smile_fee_get_ccd_mode_config(void);
+uint8_t smile_fee_get_ccd_mode2_config(void);
+uint32_t smile_fee_get_event_detection(void);
+uint32_t smile_fee_get_clear_error_flag(void);
+uint16_t smile_fee_get_ccd1_single_pixel_treshold(void);
+uint16_t smile_fee_get_ccd2_single_pixel_treshold(void);
+uint32_t smile_fee_get_execute_op(void);
+
+
+
+/* write SMILE_FEE register mirror */
+void smile_fee_set_vstart(uint16_t vstart);
+void smile_fee_set_vend(uint16_t vend);
+void smile_fee_set_charge_injection_width(uint16_t width);
+void smile_fee_set_charge_injection_gap(uint16_t width);
+void smile_fee_set_parallel_toi_period(uint16_t period);
+void smile_fee_set_parallel_clk_overlap(uint16_t overlap);
+void smile_fee_set_ccd_readout(uint32_t ccd_id, uint32_t status);
+void smile_fee_set_n_final_dump(uint16_t lines);
+void smile_fee_set_h_end(uint16_t transfers);
+void smile_fee_set_charge_injection(uint32_t mode);
+void smile_fee_set_tri_level_clk(uint32_t mode);
+void smile_fee_set_img_clk_dir(uint32_t mode);
+void smile_fee_set_clk_dir(uint32_t mode);
+void smile_fee_set_packet_size(uint16_t pkt_size);
+void smile_fee_set_int_period(uint16_t period);
+uint16_t smile_fee_get_int_period(void);
+void smile_fee_set_readout_nodes(uint32_t nodes);
+void smile_fee_set_digitise(uint32_t mode);
+void smile_fee_set_correction_bypass(uint32_t mode);
+void smile_fee_set_vod_config(uint16_t vod);
+void smile_fee_set_ccd1_vrd_config(uint16_t vrd);
+void smile_fee_set_ccd2_vrd_config(uint16_t vrd);
+void smile_fee_set_ccd3_vrd_config(uint16_t vrd);
+void smile_fee_set_ccd4_vrd_config(uint16_t vrd);
+void smile_fee_set_ccd_vgd_config(uint16_t vgd);
+void smile_fee_set_ccd_vog_config(uint16_t vog);
+void smile_fee_set_ccd_ig_hi_config(uint16_t cfg);
+void smile_fee_set_ccd_ig_lo_config(uint16_t cfg);
+void smile_fee_set_h_start(uint16_t row);
+void smile_fee_set_ccd_mode_config(uint8_t mode);
+void smile_fee_set_ccd_mode2_config(uint8_t mode);
+void smile_fee_set_event_detection(uint32_t mode);
+void smile_fee_set_clear_error_flag(uint32_t mode);
+void smile_fee_set_ccd1_single_pixel_treshold(uint16_t threshold);
+void smile_fee_set_ccd2_single_pixel_treshold(uint16_t threshold);
+void smile_fee_set_execute_op(uint32_t mode);
+
+
+
+/* setup */
+void smile_fee_ctrl_init(void);
+
+
+#endif /* _SMILE_FEE_CTRL_H_ */
diff --git a/FEE/smile_fee_demo.c b/FEE/smile_fee_demo.c
new file mode 100644
index 0000000000000000000000000000000000000000..681dfd4a9b6f7acb45eeb3b407880f89e69fe77a
--- /dev/null
+++ b/FEE/smile_fee_demo.c
@@ -0,0 +1,566 @@
+/**
+ * this is a very simple FEE <-> DPU interface demonstrator
+ *
+ * Note that we currently emulate the RMAP connection to the FEE by function
+ * calls. The simulated FEE is very stupid, it does not really do anything...
+ *
+ * Implement your own rmap_tx and rmap_rx interface functions as needed, these
+ * can be networked, use actual spacewire or whatever.
+ *
+ * WARNING: at this point, the FEE houskeeping read-out is not yet implemented
+ *	    in the library
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <smile_fee_cfg.h>
+#include <smile_fee_ctrl.h>
+#include <smile_fee_rmap.h>
+
+
+#include <rmap.h>	/* for FEE simulation */
+
+/* whatever for now ... */
+#define MAX_PAYLOAD_SIZE	4096
+
+/* XXX include extra for RMAP headers, 128 bytes is plenty */
+#undef GRSPW2_DEFAULT_MTU
+#define GRSPW2_DEFAULT_MTU (MAX_PAYLOAD_SIZE + 128)
+
+
+
+/* for our simulated fee, you can use the accessor part of the library and
+ * the stuff below to to control you own FEE simulator
+ */
+static struct smile_fee_mirror smile_fee_mem;
+
+static uint8_t *rmap_sim_reply[64]; /* same number of buffers as trans log size */
+static int rmap_sim_reply_size[64]; /* buffer sizes */
+/**
+ * @brief create a complete package from header and payload data including CRC8
+ *
+ * @note this is a helper function to generate complete binary RMAP packet dumps
+ *
+ * @param blob the blob buffer; if NULL, the function returns the needed size
+ *
+ * @param[in]  cmd an rmap command buffer
+ * @param[in]  cmd_size the size of the rmap command buffer
+ * @param[in]  non_crc_bytes leading bytes in the header not path of the CRC
+ * @param[in]  data a data buffer (may be NULL)
+ * @param[in]  data_size the size of the data buffer (ignored if data is NULL)
+ *
+ * @returns the size of the blob or 0 on error
+ */
+
+int fee_sim_package(uint8_t *blob,
+		    const uint8_t *cmd,  int cmd_size,
+		    const uint8_t non_crc_bytes,
+		    const uint8_t *data, int data_size)
+{
+	int n;
+	int has_data_crc = 0;
+	struct rmap_instruction *ri;
+
+
+
+	if (!cmd_size) {
+		blob = NULL;
+		return 0;
+	}
+
+
+	/* allocate space for header, header crc, data, data crc */
+	n = cmd_size + 1;
+
+	ri = (struct rmap_instruction *) &cmd[non_crc_bytes + RMAP_INSTRUCTION];
+
+	/* see if the type of command needs a data crc field at the end */
+	switch (ri->cmd) {
+	case RMAP_READ_ADDR_SINGLE:
+	case RMAP_READ_ADDR_INC:
+		has_data_crc = 1;
+		n += 1;
+		break;
+	default:
+		break;
+	}
+
+
+	if (data)
+		n += data_size;
+
+	if (!blob)
+		return n;
+
+
+	memcpy(&blob[0], cmd, cmd_size);
+
+	blob[cmd_size] = rmap_crc8(&cmd[non_crc_bytes],
+				   cmd_size - non_crc_bytes);
+
+	if (data) {
+		memcpy(&blob[cmd_size + 1], data, data_size);
+		blob[cmd_size + 1 + data_size] = rmap_crc8(data, data_size);
+	} else {
+		/* if no data is present, data crc is 0x0 */
+		if (has_data_crc)
+			blob[cmd_size + 1] = 0x0;
+	}
+
+
+	return n;
+}
+
+/* simulate FEE receiving data */
+static void rmap_sim_rx(uint8_t *pkt)
+{
+	int i, n;
+
+	uint8_t src;
+
+	uint8_t *hdr;
+	uint8_t *data = NULL;
+
+	int hdr_size;
+	int data_size = 0;
+
+	struct rmap_pkt *rp;
+
+
+
+	rp = rmap_pkt_from_buffer(pkt);
+
+	if (!rp) {
+		printf("conversion error!");
+		exit(0);
+	}
+
+
+	/* The rmap libary does not implement client mode, so we do the
+	 * basics here.
+	 * At the moment, we only use
+	 *	RMAP_READ_ADDR_INC and
+	 *	RMAP_WRITE_ADDR_INC_VERIFY_REPLY,
+	 * because we only implemented the config registers, so this is pretty
+	 * easy
+	 */
+
+
+	/* we reuse the packet, turn it into a response */
+	rp->ri.cmd_resp = 0;
+
+	/* WARNING: path addressing requires extra steps (leading zero removal),
+	 * this not done because it's unused in this demo
+	 */
+
+	/* flip around logical addresses */
+	src = rp->src;
+	rp->src = rp->dst;
+	rp->dst = src;
+
+	switch (rp->ri.cmd) {
+		case RMAP_READ_ADDR_INC:
+			printf("RMAP_READ_ADDR_INC\n");
+			printf("read from addr: %x, size %d \n", rp->addr, rp->data_len);
+
+			data = malloc(rp->data_len);
+			if (!data) {
+				printf("error allocating buffer\n");
+				exit(0);
+			}
+
+			data_size = rp->data_len;
+
+			/* copy the simulated register map into the data payload
+			 * This works because the register map in the FEE
+			 * starts at address 0x0
+			 */
+			memcpy(data, (&((uint8_t *) &smile_fee_mem)[rp->addr]), data_size);
+
+			break;
+
+		case RMAP_WRITE_ADDR_INC_VERIFY_REPLY:
+			printf("RMAP_WRITE_ADDR_INC_VERIFY_REPLY\n");
+			printf("write to addr: %x, size %d \n", rp->addr, rp->data_len);
+			/* copy the payload into the simulated register
+			 * map. This works because the register map in the FEE
+			 * starts at address 0x0
+			 */
+			memcpy((&((uint8_t *) &smile_fee_mem)[rp->addr]), rp->data, rp->data_len);
+
+			rp->data_len = 0; /* no data in reply */
+
+			break;
+		default:
+			printf("rmap command not implemented: %x\n", rp->ri.cmd);
+			break;
+	}
+
+	/* convert packet to buffer */
+
+	/* determine header size */
+	hdr_size = rmap_build_hdr(rp, NULL);
+
+	hdr = malloc(hdr_size);
+	if (!hdr) {
+		printf("Error allocating memory\n");
+		exit(0);
+	}
+
+	rmap_build_hdr(rp, hdr);
+
+	rmap_erase_packet(rp);
+
+
+	/* convert to blob and store in our reply array */
+
+	for (i = 0; i < 64; i++)  {
+		if (!rmap_sim_reply[i])
+			break; /* found unused slot */
+	}
+
+	if (i > 64) {
+		printf("Error, out of transmit slots\n");
+		exit(0);
+	}
+
+
+	/* determine required buffer size
+	 * WARNING non-crc bytes are hardcoded to 0 since we do not use path
+	 * addressing in this demo
+	 */
+	n = fee_sim_package(NULL, hdr, hdr_size, 0,
+			    data, data_size);
+
+	rmap_sim_reply[i] = malloc(n);
+	rmap_sim_reply_size[i] = n;
+
+	if (!rmap_sim_reply[i]) {
+		printf("Error allcating blob!\n");
+		exit(0);
+	}
+
+	/* copy packet data to buffer */
+	smile_fee_package(rmap_sim_reply[i], hdr, hdr_size, 0,
+			  data, data_size);
+
+}
+
+
+/* simulate FEE sending data */
+
+static int rmap_sim_tx(uint8_t *pkt)
+{
+	int i;
+	int size;
+	static int last = 65; /* out of bounds */
+
+
+	/* simulate some activity here by clearing the execute op flag if it
+	 * is set.
+	 */
+
+	if (smile_fee_mem.cfg_reg_24 & 0x1UL) {
+		smile_fee_mem.cfg_reg_24 &= ~0x1UL;
+	}
+
+
+	/* actual tx simulation */
+
+	if (last > 64) {
+
+		for (i = 0; i < 64; i++)  {
+			if (rmap_sim_reply[i])
+				break; /* found used slot */
+		}
+
+		if (i >= 64)
+			return 0;
+
+		last = i; /* our current reply packet */
+
+	}
+
+	/* was only a size request */
+	if (!pkt)
+		return rmap_sim_reply_size[last];
+
+	/* now deliver contents */
+	size = rmap_sim_reply_size[last];
+	memcpy(pkt, rmap_sim_reply[last], size);
+
+	free(rmap_sim_reply[last]);
+
+	/* mark slot as unused */
+	rmap_sim_reply[last] = NULL;
+
+	/* reset to ouf of bounds */
+	last = 65;
+
+	return size;
+}
+
+
+/**
+ * tx function for smile_fee_ctrl
+ *
+ * @note you may want to reimplement this function if you use a different
+ *	 SpaceWire interface or if you want transport/dump the RMAP
+ *	 packets via a different mechanism, e.g. using smile_fee_package()
+ *
+ * @warn If you use smile_fee_package() to simply dump generated RMAP command
+ *	 packets, you may run into the limit set by TRANS_LOG_SIZE, as
+ *	 transactions make an entry in the transaction log, which will only
+ *	 free up slots when an ACK with the  corresponding RMAP transaction id
+ *	 has been received. So, if you simply want to dump a set of commands,
+ *	 and run into issues, increase TRANS_LOG_SIZE by an arbitrary value
+ */
+
+static int32_t rmap_tx(const void *hdr,  uint32_t hdr_size,
+		       const uint8_t non_crc_bytes,
+		       const void *data, uint32_t data_size)
+{
+#if 1
+	int pkt_size;
+	uint8_t *blob ;
+
+
+	/* determine required buffer size */
+	pkt_size = smile_fee_package(NULL, hdr, hdr_size, non_crc_bytes,
+				     data, data_size);
+
+	blob = malloc(pkt_size);
+
+	if (!blob) {
+		printf("Error allcating blob!\n");
+		return -1;
+	}
+
+	/* copy packet data to buffer */
+	pkt_size = smile_fee_package(blob, hdr, hdr_size, non_crc_bytes,
+				     data, data_size);
+
+#if 0
+	/* print it */
+	for (i = 0; i < pkt_size; i++)
+	       printf("%02x:", blob[i]);
+	printf("\n");
+#endif
+	/* "send" to FEE */
+	rmap_sim_rx(blob);
+
+	free(blob);
+
+	return 0;
+
+#else
+	/* adapt to IASW like this */
+	CrFwPckt_t pckt; /* <- allocate and copy hdr and data into that */
+	/* maybe use smile_fee_package() for that) */
+	CrIbFeePcktHandover(CrFwPckt_t pckt)
+	return 0;
+#endif
+}
+
+
+/**
+ * rx function for smile_fee_ctrl
+ *
+ * @note you may want to reimplement this function if you use a different
+ *	 SpaceWire interface or if you want inject RMAP packets via a
+ *	 different mechanism
+ */
+
+static uint32_t rmap_rx(uint8_t *pkt)
+{
+#if 1
+	return rmap_sim_tx(pkt);
+
+#else
+	/* adapt to IASW like this */
+
+
+
+	/* WARNING: Where do I geht the packet size from when used with the
+	 *	    IASW? I guess it's CrFwPcktGetLength();
+	 *	    The underlying library expects the size of the
+	 *	    next packet to be returned when the pkt argument is NULL,
+	 *	    i.e. rmap_rx(NULL) must return the packet size and hold
+	 *	    the packet until it is called with a valid buffer.
+	 *	    Note that these calls will be immediate within the same
+	 *	    function, so you won't need to implement an actual buffer
+	 *	    mechanism. I suppose if you CrIbFeePcktCollect here
+	 *	    and store the packet reference in a static variable within
+	 *	    the function, then return the size if pkt == NULL and
+	 *	    copy it when this function is called with a pointer, you
+	 *	    should be fine.
+	 *
+	 *	    i.e. do something like this:
+	 *	    (I suppose this can be simplified)
+	 */
+
+	static CrFwPckt_t fw_pckt;
+
+	/* size request */
+	if (!pkt) {
+		if (fw_pckt)	/* pending packet */
+			return CrFwPcktGetLength(fw_pckt); /* I guess... */
+	}
+
+	if (!fw_pckt) { /* have nothing */
+		if (!CrIbIsFeePcktAvail(CR_FW_CLIENT_FEE))
+			return 0;	/* no packet */
+	}
+
+	if (!fw_pckt) /* nothing stored retrieve new */
+		fw_pckt = CrIbFeePcktCollect(CR_FW_CLIENT_FEE);
+
+	pkt_size = CrFwPcktGetLength(fw_pckt);
+
+	if (pkt) {	/* If we got a pointer, assume we can do that */
+		memcpy(pkt, fw_pckt, pkt_size);
+
+		/* free fw_pckt here (if needed) and set to NULL */
+		fw_pckt = NULL
+	}
+
+	return pkt_size;
+#endif
+}
+
+
+/**
+ * @brief save repeating 3 lines of code..
+ *
+ * @note prints abort message if pending status is non-zero after 10 retries
+ */
+
+static void sync(void)
+{
+	int cnt = 0;
+	printf("syncing...");
+	while (smile_fee_rmap_sync_status()) {
+		printf("pending: %d\n", smile_fee_rmap_sync_status());
+
+		if (cnt++ > 10) {
+			printf("aborting; de");
+			break;
+		}
+
+	}
+	printf("synced\n");
+}
+
+
+
+/**
+ * SMILE FEE commanding demonstrator, this would run on the DPU
+ * note: all values are random
+ */
+
+static void smile_fee_demo(void)
+{
+
+	printf("Configuring start of vertical row shared with charge injection\n");
+	smile_fee_set_vstart(35);
+
+	printf("Configuring duration of a parallel overlap period (TOI)\n");
+	smile_fee_set_parallel_toi_period(5);
+
+
+	printf("Syncing configured values to FEE via RMAP\n");
+	smile_fee_sync_vstart(DPU2FEE);
+	smile_fee_sync_parallel_toi_period(DPU2FEE);
+
+	printf("Waiting for sync to complete\n");
+	sync();
+
+	printf("Verifying configured values in FEE\n");
+
+	printf("Clearing local values\n");
+	smile_fee_set_vstart(0);
+	smile_fee_set_parallel_toi_period(0);
+
+	printf("Syncing configured values from FEE to DPU via RMAP\n");
+	smile_fee_sync_vstart(FEE2DPU);
+	smile_fee_sync_parallel_toi_period(FEE2DPU);
+
+	printf("Waiting for sync to complete\n");
+	sync();
+
+
+	printf("Checking values: vertical row shared with charge injection: ");
+
+	if (smile_fee_get_vstart() == 35)
+		printf("SUCCESS\n");
+	else
+		printf("FAILURE\n");
+
+
+	printf("Checking values: duration of a parallel overlap period (TOI): ");
+
+	if (smile_fee_get_parallel_toi_period() == 5)
+		printf("SUCCESS\n");
+	else
+		printf("FAILURE\n");
+
+
+	printf("Setting execute op flag to expedite operational parameters\n");
+	smile_fee_set_execute_op(1);
+
+	printf("Syncing execute op flag to FEE via RMAP\n");
+	smile_fee_sync_execute_op(DPU2FEE);
+	printf("Waiting for sync to complete\n");
+	sync();
+
+
+	printf("Waiting for FEE to complete operation\n");
+
+	while (1) {
+		printf("Syncing execute op flag from FEE to DPU via RMAP\n");
+		smile_fee_sync_execute_op(FEE2DPU);
+		printf("Waiting for sync to complete\n");
+		sync();
+
+		if (!smile_fee_get_execute_op())
+			break;
+
+		printf("FEE hast not yet completed operation\n");
+	}
+
+
+	printf("FEE operation completed\n");
+}
+
+
+
+
+
+int main(void)
+{
+#if 0
+	uint8_t dpath[] = DPATH;
+	uint8_t rpath[] = RPATH;
+#endif
+
+	/* initialise the libraries */
+	smile_fee_ctrl_init();
+	smile_fee_rmap_init(GRSPW2_DEFAULT_MTU, rmap_tx, rmap_rx);
+
+
+	/* configure rmap link (adapt values as needed) */
+
+	smile_fee_set_source_logical_address(DPU_ADDR);
+	smile_fee_set_destination_key(FEE_DEST_KEY);
+	smile_fee_set_destination_logical_address(FEE_ADDR);
+	smile_fee_set_destination_path(NULL, 0);
+	smile_fee_set_return_path(NULL, 0);
+
+	/* now run the demonstrator */
+	smile_fee_demo();
+}
diff --git a/FEE/smile_fee_rmap.c b/FEE/smile_fee_rmap.c
new file mode 100644
index 0000000000000000000000000000000000000000..8af0d3a90b7fb6afcec2f44b7cae163fc2003ec7
--- /dev/null
+++ b/FEE/smile_fee_rmap.c
@@ -0,0 +1,717 @@
+/**
+ * @file   smile_fee_rmap.c
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * @brief RMAP FEE link interface
+ *
+ *
+ * Ideally, we would use asynchronous operations here, with a transaction log
+ * that is served by another thread. However, we cannot assume that such
+ * features will be available, so we'll do this by maintaining a mirror of the
+ * FEE's registers and memory, where instead of actively blocking with get()
+ * and set() RMAP calls, they operate on the local copy and the user issues
+ * sync() calls.
+ *
+ * To monitor the syncronisation status, we maintaining a transaction log
+ * tracking the submitted command set. Response packets could be processed
+ * by interrupt (or thread), but in this variant, we process the return packets
+ * when the user calls smile_fee_ctrl_sync_status()
+ *
+ * Note that for simplicity , we assume that there is a working heap allocator
+ * available, please adapt all malloc/free calls to your needs, or ask us
+ * to do that for you.
+ 
+ * NOTE: We don't have to serve more than one FEE at any given time, so we
+ *	 track addresses and paths internally in a single instance. This also
+ *	 makes the interface less cluttered. Besides, I'm lazy.
+ *
+ *
+ * @warn when operational, we expect to have exclusive control of the SpW link
+ *
+ * TODO: RMAP command response evaluation
+ */
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <rmap.h>
+#include <smile_fee_rmap.h>
+
+
+
+static uint8_t smile_fee_addr;
+static uint8_t icu_addr;
+
+static uint8_t *dpath;	/* destination path (to the FEE) */
+static uint8_t *rpath;	/* return path (to the ICU) */
+static uint8_t dpath_len;
+static uint8_t rpath_len;
+
+static uint8_t dst_key;	/* destination command key */
+
+
+
+
+/* generic calls, functions must be provided to init() */
+static int32_t (*rmap_tx)(const void *hdr,  uint32_t hdr_size,
+			  const uint8_t non_crc_bytes,
+			  const void *data, uint32_t data_size);
+static uint32_t (*rmap_rx)(uint8_t *pkt);
+
+static int data_mtu;	/* maximum data transfer size per unit */
+
+
+
+
+
+
+
+
+
+
+/* For now we maintain a simple transaction log that works like this:
+ * we allow up to 128 transfers, simply because this is how many response
+ * packets the GRSPW2 SpW descriptor table can hold at any one time without
+ * blocking the link.
+ *
+ * Every time a new transfer is to be submitted, we grab the first free
+ * slot we encounter in the "in_use" array and use the index as the transaction
+ * identifier of the RMAP packet. (Yes, this is potentially slow, as we may have
+ * to iterate over the whole array every time if we're very busy with
+ * transmissions, so there is room for improvement.)
+ *
+ * Every time a slot is retrieved, the "pending" counter is incremented to
+ * have a fast indicator of the synchronisation status, i.e. if "pending"
+ * is not set, the synchronisation procedure is complete and the local data may
+ * be read, or the remote data has been written and further commands may may
+ * be issued.
+ *
+ * The local (mirror) start address of the requested remote address is stored
+ * into the same slot in the "local_addr" array, so we'll know where to put the
+ * data if we issue an RMAP_read command. This may be omitted for write
+ * commands.
+ *
+ * Every time a response packet is received, the data (if any) is written to the
+ * local address, using the length specified by RMAP packet, so be careful where
+ * you place your buffers or registers. On success, the "in_use" slot is cleared
+ * and the pending counter is improved.
+ *
+ * XXX: careful, no locking is used on any of the log data, so this is
+ * single-thread-use only!
+ *
+ */
+#define TRANS_LOG_SIZE 64	/* GRSPW2 TX descriptor limit */
+static struct {
+
+	uint8_t  in_use[TRANS_LOG_SIZE];
+	void    *local_addr[TRANS_LOG_SIZE];
+
+	int pending;
+} trans_log;
+
+
+/**
+ * @brief grab a slot in the transaction log
+ *
+ * @param local_addr the local memory address
+ *
+ * @returns -1 on no slots, >= 0 for the transaction id
+ */
+
+static int trans_log_grab_slot(void *local_addr)
+{
+	int i;
+	int slot = -1;
+
+	for (i = 0; i < TRANS_LOG_SIZE; i++) {
+
+		if (trans_log.in_use[i])
+			continue;
+
+		/* got one */
+		slot = i;
+		trans_log.in_use[slot] = 1;
+		trans_log.local_addr[slot] = local_addr;
+		trans_log.pending++;
+		break;
+	}
+
+	return slot;
+}
+
+
+/**
+ * @brief release a slot in the transaction log
+ *
+ * @param slot the id of the slot
+ *
+ */
+
+static void trans_log_release_slot(int slot)
+{
+
+	if (slot < 0)
+		return;
+
+	if (slot >= TRANS_LOG_SIZE)
+		return;
+
+	if (!trans_log.in_use[slot])
+		return;
+
+	trans_log.in_use[slot] = 0;
+	trans_log.pending--;
+}
+
+
+/**
+ * @brief get the local address for a slot
+ *
+ * @param slot the id of the slot
+ *
+ * @returns the address or NULL if not found/slot not in use
+ */
+
+static void *trans_log_get_addr(int slot)
+{
+	if (slot < 0)
+		return NULL;
+
+	if (slot >= TRANS_LOG_SIZE)
+		return NULL;
+
+	if (!trans_log.in_use[slot])
+		return NULL;
+
+	return trans_log.local_addr[slot];
+}
+
+/**
+ * @brief n rmap command transaction
+ *
+ * @returns number of packets processed or < 0 on error
+ */
+
+static int smile_fee_process_rx(void)
+{
+	int n;
+	int cnt = 0;
+
+	void *local_addr;
+
+	uint8_t *spw_pckt;
+
+	struct rmap_pkt *rp;
+
+
+	if (!rmap_rx)
+		return -1;
+
+	/* process all pending responses */
+	while ((n = rmap_rx(NULL))) {
+		/* we received something, allocate enough space for the packet */
+		spw_pckt = (uint8_t *) malloc(n);
+		if(!spw_pckt) {
+			printf("malloc() for packet failed!\n");
+			return -1;
+		}
+
+		/* read the packet */
+		n = rmap_rx(spw_pckt);
+		if (!n) {
+			printf("Unknown error in rmap_rx()\n");
+			free(spw_pckt);
+			return -1;
+		}
+
+		cnt++;
+
+		if (0)
+			rmap_parse_pkt(spw_pckt);
+
+		/* convert format */
+		rp = rmap_pkt_from_buffer(spw_pckt);
+		free(spw_pckt);
+
+		if (!rp) {
+			printf("Error converting to RMAP packet\n");
+			continue;
+		}
+
+		local_addr = trans_log_get_addr(rp->tr_id);
+
+		if (!local_addr) {
+			printf("warning: response packet received not in"
+			       "transaction log\n");
+			rmap_erase_packet(rp);
+			continue;
+		}
+
+		if (rp->data_len)
+			memcpy(local_addr, rp->data, rp->data_len);
+
+		trans_log_release_slot(rp->tr_id);
+		rmap_erase_packet(rp);
+	}
+
+	return cnt;
+}
+
+
+/**
+ * @brief submit an rmap command transaction
+ *
+ * @param cmd the rmap command
+ * @param cmd_size the size of the rmap command
+ * @param data the payload (may be NULL)
+ * @param data_size the size of the payload
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_submit_tx(const uint8_t *cmd,  int cmd_size,
+			const uint8_t *data, int data_size)
+{
+	/* try to process pending responses */
+	smile_fee_process_rx();
+
+	if (!rmap_tx)
+		return -1;
+
+	if (0)
+		printf("Transmitting RMAP command\n");
+
+	if (rmap_tx(cmd, cmd_size, dpath_len, data, data_size)) {
+		printf("rmap_tx() returned error!");
+		return -1;
+	}
+
+	return 0;
+}
+
+
+/**
+ * @brief generate an rmap command packet
+ *
+ * @param trans_id a transaction identifier
+ *
+ * @param cmd the command buffer; if NULL, the function returns the needed size
+ *
+ * @param rmap_cmd_type the rmap command type of the packet
+ *
+ * @param addr the address to read from or write to
+ *
+ * @param size the number of bytes to read or write
+ *
+ * @returns the size of the command data buffer or 0 on error
+ */
+
+int smile_fee_gen_cmd(uint16_t trans_id, uint8_t *cmd,
+		      uint8_t rmap_cmd_type,
+		      uint32_t addr, uint32_t size)
+{
+	int n;
+
+	struct rmap_pkt *pkt;
+
+	pkt = rmap_create_packet();
+	if (!pkt) {
+		printf("Error creating packet\n");
+		return 0;
+	}
+
+	rmap_set_dst(pkt, smile_fee_addr);
+	rmap_set_src(pkt, icu_addr);
+	rmap_set_dest_path(pkt, dpath, dpath_len);
+	rmap_set_reply_path(pkt, rpath, rpath_len);
+	rmap_set_key(pkt, dst_key);
+	rmap_set_cmd(pkt, rmap_cmd_type);
+	rmap_set_tr_id(pkt, trans_id);
+	rmap_set_data_addr(pkt, addr);
+	rmap_set_data_len(pkt, size);
+
+	/* determine header size */
+	n = rmap_build_hdr(pkt, NULL);
+
+	if (!cmd) {
+		rmap_erase_packet(pkt);
+		return n;
+	}
+
+	bzero(cmd, n);
+
+	n = rmap_build_hdr(pkt, cmd);
+
+	rmap_erase_packet(pkt);
+
+	return n;
+}
+
+
+
+
+
+/**
+ * @brief submit a sync command
+ *
+ * @param fn the FEE command generation function
+ * @param addr the local address of the corresponding remote address
+ * @param data_len the length of the data payload (0 for read commands)
+ *
+ * @return 0 on success, otherwise error
+ */
+
+
+int smile_fee_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd),
+		   void *addr, int data_len)
+{
+	int n;
+	int slot;
+
+	uint8_t *rmap_cmd;
+
+
+
+	slot = trans_log_grab_slot(addr);
+	if (slot < 0)
+		return -1;
+
+
+	/* determine size of command */
+	n = fn(slot, NULL);
+
+	rmap_cmd = (uint8_t *) malloc(n);
+	if (!rmap_cmd) {
+		printf("Error allocating rmap cmd");
+		return -1;
+	}
+
+	/* now fill actual command */
+	n = fn(slot, rmap_cmd);
+	if (!n) {
+		printf("Error creating command packet\n");
+		free(rmap_cmd);
+		return -1;
+	}
+
+	n = smile_fee_submit_tx(rmap_cmd, n, addr, data_len);
+	free(rmap_cmd);
+
+	return n;
+}
+
+
+
+/**
+ * @brief submit a data sync command
+ *
+ * @param fn a FEE data transfer generation function
+ * @param addr the remote address
+ * @param data the local data address
+ * @param data_len the length of the data payload
+ * @param read 0: write, otherwise read
+ *
+ * @return 0 on success, < 0: error, > 0: retry
+ *
+ * @note this one is a little redundant, but otherwise we'd have a lot of
+ *	 unused parameters on most of the control functions
+ *
+ * XXX need a paramter for read...meh...must think of something else
+ */
+
+
+int smile_fee_sync_data(int (*fn)(uint16_t trans_id, uint8_t *cmd,
+				  uint32_t addr, uint32_t data_len),
+			uint32_t addr, void *data, uint32_t data_len, int read)
+{
+	int n;
+	int slot;
+
+	uint8_t *rmap_cmd;
+
+
+
+	smile_fee_process_rx();
+
+	slot = trans_log_grab_slot(data);
+	if (slot < 0) {
+		if (0)
+			printf("Error: all slots busy!\n");
+		return 1;
+	}
+
+
+	/* determine size of command */
+	n = fn(slot, NULL, addr, data_len);
+
+	rmap_cmd = (uint8_t *) malloc(n);
+	if (!rmap_cmd) {
+		printf("Error allocating rmap cmd");
+		return -1;
+	}
+
+	/* now fill actual command */
+	n = fn(slot, rmap_cmd, addr, data_len);
+	if (!n) {
+		printf("Error creating command packet\n");
+		free(rmap_cmd);
+		return -1;
+	}
+
+	if (read)
+		n = smile_fee_submit_tx(rmap_cmd, n, NULL, 0);
+	else
+		n = smile_fee_submit_tx(rmap_cmd, n, data, data_len);
+
+	free(rmap_cmd);
+
+	return n;
+}
+
+
+
+/**
+ * @brief create a complete package from header and payload data including CRC8
+ *
+ * @note this is a helper function to generate complete binary RMAP packet dumps
+ *
+ * @param blob the blob buffer; if NULL, the function returns the needed size
+ *
+ * @param[in]  cmd an rmap command buffer
+ * @param[in]  cmd_size the size of the rmap command buffer
+ * @param[in]  non_crc_bytes leading bytes in the header not path of the CRC
+ * @param[in]  data a data buffer (may be NULL)
+ * @param[in]  data_size the size of the data buffer (ignored if data is NULL)
+ *
+ * @returns the size of the blob or 0 on error
+ */
+
+int smile_fee_package(uint8_t *blob,
+		      const uint8_t *cmd,  int cmd_size,
+		      const uint8_t non_crc_bytes,
+		      const uint8_t *data, int data_size)
+{
+	int n;
+	int has_data_crc = 0;
+	struct rmap_instruction *ri;
+
+
+
+	if (!cmd_size) {
+		blob = NULL;
+		return 0;
+	}
+
+
+	/* allocate space for header, header crc, data, data crc */
+	n = cmd_size + 1;
+
+	ri = (struct rmap_instruction *) &cmd[non_crc_bytes + RMAP_INSTRUCTION];
+
+	/* see if the type of command needs a data crc field at the end */
+	switch (ri->cmd) {
+	case RMAP_READ_MODIFY_WRITE_ADDR_INC:
+	case RMAP_WRITE_ADDR_SINGLE:
+	case RMAP_WRITE_ADDR_INC:
+	case RMAP_WRITE_ADDR_SINGLE_VERIFY:
+	case RMAP_WRITE_ADDR_INC_VERIFY:
+		has_data_crc = 1;
+		n += 1;
+		break;
+	default:
+		break;
+	}
+
+
+	if (data)
+		n += data_size;
+
+	if (!blob)
+		return n;
+
+
+	memcpy(&blob[0], cmd, cmd_size);
+
+	blob[cmd_size] = rmap_crc8(&cmd[non_crc_bytes],
+				   cmd_size - non_crc_bytes);
+
+	if (data) {
+		memcpy(&blob[cmd_size + 1], data, data_size);
+		blob[cmd_size + 1 + data_size] = rmap_crc8(data, data_size);
+	} else {
+		/* if no data is present, data crc is 0x0 */
+		if (has_data_crc)
+			blob[cmd_size + 1] = 0x0;
+	}
+
+
+	return n;
+}
+
+
+/**
+ * @brief sets the logical address of the FEE
+ * @param addr the address
+ */
+
+void smile_fee_set_destination_logical_address(uint8_t addr)
+{
+	smile_fee_addr = addr;
+}
+
+/**
+ * @brief sets the logical address of the ICU
+ * @param addr the address
+ */
+
+void smile_fee_set_source_logical_address(uint8_t addr)
+{
+	icu_addr = addr;
+}
+
+
+/**
+ * @brief set the destination path to the FEE
+ * @param path a byte array containing the path (may be NULL)
+ * @param len the number of elements in the array
+ *
+ * @returns 0 on success, otherwise error
+ *
+ * @note the path array is taken as a reference, make sure to keep it around
+ *	 the maximum length of the path is 15 elements
+ *	 setting either path NULL or len 0 disables destination path addressing
+ */
+
+int smile_fee_set_destination_path(uint8_t *path, uint8_t len)
+{
+	if (len > RMAP_MAX_PATH_LEN)
+		return -1;
+
+	if (!path || !len) {
+		dpath     = NULL;
+		dpath_len = 0;
+		return 0;
+	}
+
+	dpath     = path;
+	dpath_len = len;
+
+	return 0;
+}
+
+
+/**
+ * @brief set the return path to the ICU
+ * @param path a byte array containing the path (may be NULL)
+ * @param len the number of elements in the array
+ *
+ * @returns 0 on success, otherwise error
+ *
+ * @note the path array is taken as a reference, make sure to keep it around
+ *	 the maximum length of the path is 12 elements
+ *	 the number of elements must be a multiple of 4 (due to RMAP protocol)
+ *	 setting either path NULL or len 0 disables return path addressing
+ */
+
+int smile_fee_set_return_path(uint8_t *path, uint8_t len)
+{
+	if (len > RMAP_MAX_REPLY_PATH_LEN)
+		return -1;
+
+	if (!path || !len) {
+		rpath     = NULL;
+		rpath_len = 0;
+		return 0;
+	}
+
+	rpath     = path;
+	rpath_len = len;
+
+	return 0;
+}
+
+
+/**
+ * @brief set the destination command key to use
+ *
+ * @param key the destination key
+ */
+
+void smile_fee_set_destination_key(uint8_t key)
+{
+	dst_key = key;
+}
+
+
+/**
+ * @brief get the FEE <-> ICU mirror RMAP synchronisation status
+ *
+ * @returns 0: synchronised, > 0: operations pending
+ */
+
+int smile_fee_rmap_sync_status(void)
+{
+	/* try to process pending responses */
+	smile_fee_process_rx();
+
+	return trans_log.pending;
+}
+
+
+/**
+ * @brief reset all entries in the RMAP transaction log
+ */
+
+void smile_fee_rmap_reset_log(void)
+{
+	bzero(trans_log.in_use, TRANS_LOG_SIZE);
+	trans_log.pending = 0;
+}
+
+
+/**
+ * @brief initialise the fee control library
+ *
+ * @param mtu the maximum data transfer size per unit
+ *
+ * @param rmap_tx a function pointer to transmit an rmap command
+ * @param rmap_rx function pointer to receive an rmap command
+ *
+ * @note rmap_tx is expected to return 0 on success
+ *	 rmap_rx is expected to return the number of packet bytes
+ *
+ * @returns 0 on success, otherwise error
+ */
+
+int smile_fee_rmap_init(int mtu,
+			int32_t (*tx)(const void *hdr,  uint32_t hdr_size,
+				      const uint8_t non_crc_bytes,
+				      const void *data, uint32_t data_size),
+			uint32_t (*rx)(uint8_t *pkt))
+{
+	if (!tx)
+		return -1;
+
+	if (!rx)
+		return -1;
+
+	rmap_tx = tx;
+	rmap_rx = rx;
+
+	data_mtu = mtu;
+
+	return 0;
+}
diff --git a/FEE/smile_fee_rmap.h b/FEE/smile_fee_rmap.h
new file mode 100644
index 0000000000000000000000000000000000000000..0efd99343ff16d4fcb63faadc70e3ada5836dee8
--- /dev/null
+++ b/FEE/smile_fee_rmap.h
@@ -0,0 +1,64 @@
+/**
+ * @file   smile_fee_rmap.h
+ * @author Armin Luntzer (armin.luntzer@univie.ac.at),
+ * @date   2018
+ *
+ * @copyright GPLv2
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ */
+
+#ifndef _SMILE_FEE_RMAP_H_
+#define _SMILE_FEE_RMAP_H_
+
+#include <stdint.h>
+
+
+
+int smile_fee_submit_tx(const uint8_t *cmd,  int cmd_size,
+			const uint8_t *data, int data_size);
+
+
+int smile_fee_gen_cmd(uint16_t trans_id, uint8_t *cmd,
+		      uint8_t rmap_cmd_type,
+		      uint32_t addr, uint32_t size);
+
+int smile_fee_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd),
+		   void *addr, int data_len);
+
+int smile_fee_sync_data(int (*fn)(uint16_t trans_id, uint8_t *cmd,
+				  uint32_t addr, uint32_t data_len),
+			uint32_t addr, void *data, uint32_t data_len, int read);
+
+int smile_fee_package(uint8_t *blob,
+		      const uint8_t *cmd,  int cmd_size,
+		      const uint8_t non_crc_bytes,
+		      const uint8_t *data, int data_size);
+
+void smile_fee_set_destination_logical_address(uint8_t addr);
+
+int smile_fee_set_destination_path(uint8_t *path, uint8_t len);
+int smile_fee_set_return_path(uint8_t *path, uint8_t len);
+void smile_fee_set_source_logical_address(uint8_t addr);
+void smile_fee_set_destination_key(uint8_t key);
+
+int smile_fee_rmap_sync_status(void);
+
+void smile_fee_rmap_reset_log(void);
+
+int smile_fee_rmap_init(int mtu,
+			int32_t (*tx)(const void *hdr,  uint32_t hdr_size,
+				      const uint8_t non_crc_bytes,
+				      const void *data, uint32_t data_size),
+			uint32_t (*rx)(uint8_t *pkt));
+
+
+
+#endif /* _SMILE_FEE_RMAP_H_ */