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_ */