From 87a92212cb3d42eb36f70f9edefa7f2e51eb1d4d Mon Sep 17 00:00:00 2001 From: Dominik Loidolt <dominik.loidolt@univie.ac.at> Date: Fri, 23 Apr 2021 17:20:54 +0200 Subject: [PATCH] discard old file format. Now the only input format is like: 12 AB 23 CD .. .. change to new compression and decompression library --- .gitignore | 51 + CHANGELOG.md | 49 + Makefile | 36 + cmp_tool.c | 436 ++++++ include/byteorder.h | 273 ++++ include/cmp_data_types.h | 190 +++ include/cmp_debug.h | 34 + include/cmp_icu.h | 28 + include/cmp_rdcu.h | 43 + include/cmp_rdcu_extended.h | 30 + include/cmp_support.h | 184 +++ include/decmp.h | 31 + include/rdcu_cmd.h | 162 +++ include/rdcu_ctrl.h | 284 ++++ include/rdcu_pkt_to_file.h | 39 + include/rdcu_rmap.h | 65 + include/rmap.h | 217 +++ include/tool_lib.h | 46 + lib/cmp_data_types.c | 647 +++++++++ lib/cmp_icu.c | 1781 +++++++++++++++++++++++++ lib/cmp_rdcu.c | 876 ++++++++++++ lib/cmp_support.c | 595 +++++++++ lib/decmp.c | 1534 +++++++++++++++++++++ lib/rdcu_cmd.c | 886 +++++++++++++ lib/rdcu_ctrl.c | 2491 +++++++++++++++++++++++++++++++++++ lib/rdcu_pkt_to_file.c | 472 +++++++ lib/rdcu_rmap.c | 822 ++++++++++++ lib/rmap.c | 778 +++++++++++ lib/tool_lib.c | 1206 +++++++++++++++++ test_data/test_data1.dat | 4 + test_data/test_data2.dat | 4 + 31 files changed, 14294 insertions(+) create mode 100644 .gitignore create mode 100644 CHANGELOG.md create mode 100644 Makefile create mode 100755 cmp_tool.c create mode 100644 include/byteorder.h create mode 100644 include/cmp_data_types.h create mode 100644 include/cmp_debug.h create mode 100644 include/cmp_icu.h create mode 100644 include/cmp_rdcu.h create mode 100644 include/cmp_rdcu_extended.h create mode 100644 include/cmp_support.h create mode 100644 include/decmp.h create mode 100644 include/rdcu_cmd.h create mode 100644 include/rdcu_ctrl.h create mode 100644 include/rdcu_pkt_to_file.h create mode 100644 include/rdcu_rmap.h create mode 100644 include/rmap.h create mode 100644 include/tool_lib.h create mode 100644 lib/cmp_data_types.c create mode 100644 lib/cmp_icu.c create mode 100644 lib/cmp_rdcu.c create mode 100644 lib/cmp_support.c create mode 100644 lib/decmp.c create mode 100644 lib/rdcu_cmd.c create mode 100644 lib/rdcu_ctrl.c create mode 100644 lib/rdcu_pkt_to_file.c create mode 100644 lib/rdcu_rmap.c create mode 100644 lib/rmap.c create mode 100644 lib/tool_lib.c create mode 100644 test_data/test_data1.dat create mode 100644 test_data/test_data2.dat diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..3158f7e --- /dev/null +++ b/.gitignore @@ -0,0 +1,51 @@ +### C ### +# Executables +cmp_tool + +# Prerequisites +*.d + +# Object files +*.o +*.ko +*.obj +*.elf + +# Linker output +*.ilk +*.map +*.exp + +# Precompiled Headers +*.gch +*.pch + +# Libraries +*.lib +*.a +*.la +*.lo + +# Shared objects (inc. Windows DLLs) +*.dll +*.so +*.so.* +*.dylib + +# Executables +*.exe +*.out +*.app +*.i*86 +*.x86_64 +*.hex + +# Debug files +*.dSYM/ +*.su +*.idb +*.pdb + +# C/C++/ObjC language server +.ccls-cache + diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..fdbf871 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,49 @@ +# Changelog +All notable changes to this project will be documented in this file. + +## [Unreleased] +## [0.05] - 23-04-2021 +### Removed +- discard old file format. Now the only input format is like: 12 AB 23 CD .. .. +### Changed +- change to new compression and decompression library +## [0.05 Beta 2] - 04-12-2020 +### Fixed +- packet file number now 4 digits long +- now the mtu size in .rdcu_pkt_mode_cfg file for the data packets +## [0.05 Beta] - 17-11-2020 +### Fixed +- fixes small errors when reading the data +### Added +- add frame script to mange multiple compression in a raw +- add last_info option to generate RMAP packets to read the last compression results in parallel +## [0.04] - 12-08-2020 +### Fixed +- fixes an error when reading compressed data for decompression when compiled for a 32-bit system where a long integer has 4 bytes +- fixes a bug that generates wrong packets for reading data from the RDCU with the --rdcu_pkt option +## [0.03] - 07-07-2020 +### Added +- README: add a note for the --rdcu_pkt option +### Fixed +- if the --rdcu_pkt option is set the program did not create the TC_FILES directory for the tc files +- if the --rdcu_pkt option is set the program did not build the data and model packets correctly. +- if the --rdcu_pkt option is set the program now also sets the RDCU Interrupt Enable bit. +- fix typo in Help and README +## [0.02] - 02-06-2020 +### Added +- add --rdcu_pkt option to generate the packets to set a RDCU compression +- add --model_cfg, --diff_cfg option to print default model/1d-diff configuration +- add --rdcu_par option to add the RDCU parameters to compression configuration and decompression information +- check the cfg file and the info file for plausibility +### Changed +- change .info file format +### Fixed +- include fixes of rmap lib +### Removed +- removes comma symbol as indicator for a comment + +## [0.01] - 06-05-2020 +### Added +- initialte realse + + diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..d617c78 --- /dev/null +++ b/Makefile @@ -0,0 +1,36 @@ +CC = gcc +SOURCEDIR = lib +INCLUDEDIR = include +BUILDDIR = ./ +CFLAGS := -Wall -Wextra -std=gnu99 -pedantic -Wshadow +RELCFLAGS := -O2 # Release flags +DBCFLAGS := -O0 -g3 #debug flags +CPPFLAGS := -I $(INCLUDEDIR) +LDFLAGS := -lm +SOURCES := cmp_tool.c \ + $(SOURCEDIR)/rmap.c \ + $(SOURCEDIR)/rdcu_ctrl.c \ + $(SOURCEDIR)/rdcu_cmd.c \ + $(SOURCEDIR)/rdcu_rmap.c \ + $(SOURCEDIR)/cmp_support.c \ + $(SOURCEDIR)/cmp_data_types.c \ + $(SOURCEDIR)/cmp_rdcu.c \ + $(SOURCEDIR)/cmp_icu.c \ + $(SOURCEDIR)/decmp.c \ + $(SOURCEDIR)/rdcu_pkt_to_file.c \ + $(SOURCEDIR)/tool_lib.c +TARGET := cmp_tool + +DEBUG?=1 +ifeq "$(shell expr $(DEBUG) \> 1)" "1" + CFLAGS += -DDEBUGLEVEL=$(DEBUG) +else + CFLAGS += -DDEBUGLEVEL=1 +endif + + +all: $(SOURCES) + $(CC) $(CPPFLAGS) $(CFLAGS) $(RELCFLAGS) $^ -o $(TARGET) $(LDFLAGS) + +debug: $(SOURCES) + $(CC) $(CPPFLAGS) $(CFLAGS) $(DBCFLAGS) $^ -o $(TARGET) $(LDFLAGS) diff --git a/cmp_tool.c b/cmp_tool.c new file mode 100755 index 0000000..90ce973 --- /dev/null +++ b/cmp_tool.c @@ -0,0 +1,436 @@ +/** + * @file cmp_tool.c + * @author Johannes Seelig (johannes.seelig@univie.ac.at) + * @author Dominik Loidolt (dominik.loidolt@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 command line tool for PLATO ICU/RDCU compression/decompression + * @see README.md + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + +#include <limits.h> +#include <getopt.h> + +#include "include/tool_lib.h" +#include "include/cmp_icu.h" +#include "include/cmp_rdcu.h" +#include "include/decmp.h" + +#define VERSION "0.05" + +/* + * For long options that have no equivalent short option, use a + * non-character as a pseudo short option, starting with CHAR_MAX + 1. + */ +enum { + RDCU_PKT_OPTION = CHAR_MAX + 1, + DIFF_CFG_OPTION, + RDCU_PAR_OPTION, + LAST_INFO, +}; + +static struct option const long_options[] = { + {"rdcu_par", no_argument, NULL, 'a'}, + {"model_cfg", no_argument, NULL, 'n'}, + {"help", no_argument, NULL, 'h'}, + {"verbose", no_argument, NULL, 'v'}, + {"version", no_argument, NULL, 'V'}, + {"rdcu_pkt", no_argument, NULL, RDCU_PKT_OPTION}, + {"diff_cfg", no_argument, NULL, DIFF_CFG_OPTION}, + {"last_info", required_argument, NULL, LAST_INFO} +}; + + +/** + * @brief This is the main function of the compression / decompression tool + * + * @param argc argument count + * @param argv argument vector + * @see README.md + * + * @returns EXIT_SUCCESS on success, EXIT_FAILURE on error + */ + +int main(int argc, char **argv) +{ + int32_t opt; + + const char *cfg_file_name = NULL; + const char *info_file_name = NULL; + const char *data_file_name = NULL; + const char *model_file_name = NULL; + const char *last_info_file_name = NULL; + const char *output_prefix = DEFAULT_OUTPUT_PREFIX; + + int print_model_cfg = 0; + int print_diff_cfg = 0; + int print_rdcu_cfg = 1; + int cmp_operation = 0; + int verbose_en = 0; + int rdcu_pkt_mode = 0; + + struct cmp_cfg cfg = {0}; /* compressor configuration struct */ + struct cmp_info info = {0}; /* decompression information struct */ + struct cmp_info last_info = {0}; /* last decompression information struct */ + int error; + + uint32_t *decomp_input_buf = NULL; + uint16_t *input_model_buf = NULL; + + /* show help if no arguments are provided */ + if (argc < 2) { + Print_Help(*argv); + exit(EXIT_FAILURE); + } + + while ((opt = getopt_long (argc, argv, "ac:d:hi:m:no:vV", long_options, + NULL)) != -1) { + switch (opt) { + case 'a': /* --rdcu_par */ + print_rdcu_cfg = 1; + break; + case 'c': + cmp_operation = 1; + cfg_file_name = optarg; + break; + case 'd': + data_file_name = optarg; + break; + case 'h': /* --help */ + Print_Help(*argv); + exit(EXIT_SUCCESS); + break; + case 'i': + info_file_name = optarg; + break; + case 'm': /* read model */ + model_file_name = optarg; + break; + case 'n': /* --model_cfg */ + print_model_cfg = 1; + break; + case 'o': + output_prefix = optarg; + break; + case 'v': /* --verbose */ + verbose_en = 1; + break; + case 'V': /* --version */ + printf("%s %s\n", PROGRAM_NAME, VERSION); + exit(EXIT_SUCCESS); + break; + case DIFF_CFG_OPTION: + print_diff_cfg = 1; + break; + case RDCU_PKT_OPTION: + rdcu_pkt_mode = 1; + break; + case LAST_INFO: + rdcu_pkt_mode = 1; + last_info_file_name = optarg; + break; + default: + Print_Help(*argv); + exit(EXIT_FAILURE); + break; + } + } + + if (print_model_cfg == 1) { + print_cfg(&DEFAULT_CFG_MODEL, print_rdcu_cfg); + exit(EXIT_SUCCESS); + } + + if (print_diff_cfg == 1) { + print_cfg(&DEFAULT_CFG_DIFF, print_rdcu_cfg); + exit(EXIT_SUCCESS); + } + + printf("#########################################################\n"); + printf("### PLATO Compression/Decompression Tool Version %s ###\n", + VERSION); + printf("#########################################################\n\n"); + + if (!cfg_file_name && !info_file_name) { + fprintf(stderr, "%s: No configuration file (-c option) or decompression information file (-i option) specified.\n", + PROGRAM_NAME); + exit(EXIT_FAILURE); + } + + if (!data_file_name) { + fprintf(stderr, "%s: No data file (-d option) specified.\n", + PROGRAM_NAME); + exit(EXIT_FAILURE); + } + + /* read input data and .cfg or .info */ + if (cmp_operation) { /* compression mode */ + printf("### Compression ###\n"); + printf("Importing configuration file %s ... ", cfg_file_name); + + error = read_cmp_cfg(cfg_file_name, &cfg, verbose_en); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + + printf("Importing data file %s ... ", data_file_name); + + cfg.input_buf = read_file16(data_file_name, cfg.samples, + verbose_en); + if (!cfg.input_buf) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + } else { /* decompression mode*/ + uint32_t cmp_size_byte; + + printf("### Decompression ###\n\n"); + printf("Importing decompression information file ... "); + + error = read_cmp_info(info_file_name, &info, verbose_en); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + + printf("Importing compressed data file %s ... ", data_file_name); + + cmp_size_byte = (info.cmp_size + 31) / 8; + + decomp_input_buf = read_file32(data_file_name, cmp_size_byte/4, + verbose_en); + if (!decomp_input_buf) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + } + + /* read in model for compressed and decompression */ + if ((cmp_operation && model_mode_is_used(cfg.cmp_mode)) || + (!cmp_operation && model_mode_is_used(info.cmp_mode_used))) { + uint32_t model_length; + + if (cmp_operation) + model_length = cfg.samples; + else + model_length = info.samples_used; + + if (!model_file_name) { + fprintf(stderr, "%s: No model file (-m " + "option) specified.\n", PROGRAM_NAME); + exit(EXIT_FAILURE); + } + + printf("Importing model file %s ... ", model_file_name); + + + input_model_buf = read_file16(model_file_name, model_length, + verbose_en); + if (!input_model_buf) { + printf("FAILED\n"); + free(cfg.input_buf); + free(decomp_input_buf); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + if (cmp_operation) + cfg.model_buf = input_model_buf; + } + + if (cmp_operation) { /* perform a compression */ + uint32_t cmp_size_byte; + + if (rdcu_pkt_mode) { + printf("Generate compression setup packets ... \n"); + error = init_rmap_pkt_to_file(); + if (error) { + printf("... FAILED\n"); + fprintf(stderr, "%s: Read RMAP packet config file .rdcu_pkt_mode_cfg failed.\n", + PROGRAM_NAME); + free(cfg.input_buf); + free(input_model_buf); + exit(EXIT_FAILURE); + } + + if (last_info_file_name) { + error = read_cmp_info(last_info_file_name, + &last_info, verbose_en); + if (error) { + printf("... FAILED\n"); + fprintf(stderr, "%s: %s: Importing last decompression information file failed.\n", + PROGRAM_NAME, last_info_file_name); + exit(EXIT_FAILURE); + } + + error = gen_rdcu_parallel_pkts(&cfg, &last_info); + if (error) { + printf("... FAILED\n"); + free(cfg.input_buf); + free(input_model_buf); + exit(EXIT_FAILURE); + } + } + + error = gen_write_rdcu_pkts(&cfg); + + if (error) { + printf("... FAILED\n"); + free(cfg.input_buf); + free(input_model_buf); + exit(EXIT_FAILURE); + } else + printf("... DONE\n"); + } + + cfg.icu_output_buf = (uint32_t *)malloc(cfg.buffer_length * + SAM2BYT); + if (cfg.icu_output_buf == NULL) { + printf("%s: Error allocating Memory for Output Buffer." + "\n", PROGRAM_NAME); + free(cfg.input_buf); + free(input_model_buf); + abort(); + } + + printf("Compress data ... "); + + error = icu_compress_data(&cfg, &info); + free(cfg.input_buf); + if (error || info.cmp_err != 0) { + printf("FAILED\n"); + printf("Compression error %#X\n", info.cmp_err); + free(input_model_buf); + free(cfg.icu_output_buf); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + if (rdcu_pkt_mode) { + printf("Generate the read results packets ... "); + error = gen_read_rdcu_pkts(&info); + if (error) { + printf("FAILED\n"); + free(input_model_buf); + free(cfg.icu_output_buf); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + } + printf("Write compressed data to file %s.cmp ... ", + output_prefix); + + /* length of cmp_size in bytes words (round up to 4 bytes) */ + cmp_size_byte = (info.cmp_size + 31)/32 * 4; + + error = write_cmp_data_file(cfg.icu_output_buf, cmp_size_byte, + output_prefix, ".cmp", verbose_en); + free(cfg.icu_output_buf); + if (error) { + printf("FAILED\n"); + free(input_model_buf); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + if (model_mode_is_used(cfg.cmp_mode)) { + printf("Write updated model to file %s_upmodel.dat ... " + , output_prefix); + error = write_to_file16(input_model_buf, info.samples_used, + output_prefix, "_upmodel.dat", + verbose_en); + free(input_model_buf); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + } + + printf("Write decompression information to file %s.info ... ", + output_prefix); + + error = write_info(&info, output_prefix, print_rdcu_cfg); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + if (verbose_en) { + printf("\n"); + print_cmp_info(&info); + printf("\n"); + } + } else { /* perform a decompression */ + uint16_t *decomp_output; + + decomp_output = (uint16_t *)malloc(info.samples_used * SAM2BYT); + if (decomp_output == NULL) { + printf("Error allocating Memory for decmpr Model Buffer\n"); + free(decomp_input_buf); + free(input_model_buf); + abort(); + } + + printf("Decompress data ... "); + + error = decompress_data(decomp_input_buf, input_model_buf, + &info, decomp_output); + free(decomp_input_buf); + if (error != 0) { + free(decomp_output); + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + printf("Write decompressed data to file %s.dat ... ", + output_prefix); + + error = write_to_file16(decomp_output, info.samples_used, + output_prefix, ".dat", verbose_en); + free(decomp_output); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + + if (model_mode_is_used(info.cmp_mode_used)) { + printf("Write updated model to file %s_upmodel.dat ... " + , output_prefix); + error = write_to_file16(input_model_buf, + info.samples_used, + output_prefix, "_upmodel.dat", + verbose_en); + free(input_model_buf); + if (error) { + printf("FAILED\n"); + exit(EXIT_FAILURE); + } else + printf("DONE\n"); + } + } + + exit(EXIT_SUCCESS); +} diff --git a/include/byteorder.h b/include/byteorder.h new file mode 100644 index 0000000..b039705 --- /dev/null +++ b/include/byteorder.h @@ -0,0 +1,273 @@ +/** + * @file byteorder.h + * @author Armin Luntzer (armin.luntzer@univie.ac.at), + * @date 2015 + * + * @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. + * + * This is a set of macros for consistent endianess conversion. They work + * for both little and big endian cpus. + * + * conversion of XX-bit integers (16- or 32-) between native CPU format + * and little/big endian format: + * cpu_to_[bl]eXX(uintXX_t x) + * [bl]eXX_to_cpu(uintXX_t x) + * + * the same, but change in situ: + * cpu_to_[bl]eXXs(uintXX_t x) + * [bl]eXX_to_cpus(uintXX_t x) + * + * + * This is based on the byte order macros from the linux kernel, see: + * include/linux/byteorder/generic.h + * include/uapi/linux/swab.h + * include/uapi/linux/byteorder/big_endian.h + * include/uapi/linux/byteorder/little_endian.h + * by @author Linus Torvalds et al. + * + */ +#ifndef BYTEORDER_H +#define BYTEORDER_H + +#include <stdint.h> + + + +#ifdef __BIG_ENDIAN +#undef __BIG_ENDIAN +#endif + +#ifdef __LITTLE_ENDIAN +#undef __LITTLE_ENDIAN +#endif + +#if (__sparc__) +#ifndef __BIG_ENDIAN +#define __BIG_ENDIAN 4321 +#endif +#endif + +#if (__i386__ || __x86_64__) +#ifndef __LITTLE_ENDIAN +#define __LITTLE_ENDIAN 1234 +#endif +#endif + + +#define ___constant_swab16(x) ((uint16_t)( \ + (((uint16_t)(x) & (uint16_t)0x00ffU) << 8) | \ + (((uint16_t)(x) & (uint16_t)0xff00U) >> 8))) + +#define ___constant_swab32(x) ((uint32_t)( \ + (((uint32_t)(x) & (uint32_t)0x000000ffUL) << 24) | \ + (((uint32_t)(x) & (uint32_t)0x0000ff00UL) << 8) | \ + (((uint32_t)(x) & (uint32_t)0x00ff0000UL) >> 8) | \ + (((uint32_t)(x) & (uint32_t)0xff000000UL) >> 24))) + + +#ifdef USE_BUILTIN_BSWAP +#if GCC_VERSION >= 40400 +#define __HAVE_BUILTIN_BSWAP32__ +#endif +#if GCC_VERSION >= 40800 +#define __HAVE_BUILTIN_BSWAP16__ +#endif +#endif /* USE_BUILTIN_BSWAP */ + + +static inline __attribute__((const)) uint16_t __fswab16(uint16_t val) +{ +#ifdef __HAVE_BUILTIN_BSWAP16__ + return __builtin_bswap16(val); +#else + return ___constant_swab16(val); +#endif +} + + +static inline __attribute__((const)) uint32_t __fswab32(uint32_t val) +{ +#ifdef __HAVE_BUILTIN_BSWAP32__ + return __builtin_bswap32(val); +#else + return ___constant_swab32(val); +#endif +} + + +/** + * @brief return a byteswapped 16-bit value + * @param x value to byteswap + */ + +#define __swab16(x) \ + (__builtin_constant_p((uint16_t)(x)) ? \ + ___constant_swab16(x) : \ + __fswab16(x)) + + +/** + * @brief return a byteswapped 32-bit value + * @param x a value to byteswap + */ + +#define __swab32(x) \ + (__builtin_constant_p((uint32_t)(x)) ? \ + ___constant_swab32(x) : \ + __fswab32(x)) + + +/** + * @brief return a byteswapped 16-bit value from a pointer + * @param p a pointer to a naturally-aligned 16-bit value + */ +static inline uint16_t __swab16p(const uint16_t *p) +{ + return __swab16(*p); +} + + +/** + * @brief return a byteswapped 32-bit value from a pointer + * @param p a pointer to a naturally-aligned 32-bit value + */ +static inline uint32_t __swab32p(const uint32_t *p) +{ + return __swab32(*p); +} + + +/** + * @brief byteswap a 16-bit value in-place + * @param p a pointer to a naturally-aligned 16-bit value + */ + +static inline void __swab16s(uint16_t *p) +{ + *p = __swab16p(p); +} + + +/** + * @brief byteswap a 32-bit value in-place + * @param p a pointer to a naturally-aligned 32-bit value + */ + +static inline void __swab32s(uint32_t *p) +{ + *p = __swab32p(p); +} + + + +#ifdef __BIG_ENDIAN + +#define __cpu_to_le16(x) ((uint16_t)__swab16((x))) +#define __cpu_to_le32(x) ((uint32_t)__swab32((x))) + +#define __cpu_to_le16s(x) __swab16s((x)) +#define __cpu_to_le32s(x) __swab32s((x)) + +#define __cpu_to_be16(x) ((uint16_t)(x)) +#define __cpu_to_be32(x) ((uint32_t)(x)) + +#define __cpu_to_be16s(x) { (void)(x); } +#define __cpu_to_be32s(x) { (void)(x); } + + + +#define __le16_to_cpu(x) __swab16((uint16_t)(x)) +#define __le32_to_cpu(x) __swab32((uint32_t)(x)) + +#define __le16_to_cpus(x) __swab16s((x)) +#define __le32_to_cpus(x) __swab32s((x)) + +#define __be16_to_cpu(x) ((uint16_t)(x)) +#define __be32_to_cpu(x) ((uint32_t)(x)) + +#define __be16_to_cpus(x) { (void)(x); } +#define __be32_to_cpus(x) { (void)(x); } + +#endif /* __BIG_ENDIAN */ + + +#ifdef __LITTLE_ENDIAN + +#define __cpu_to_le16(x) ((uint16_t)(x)) +#define __cpu_to_le32(x) ((uint32_t)(x)) + +#define __cpu_to_le16s(x) { (void)(x); } +#define __cpu_to_le32s(x) { (void)(x); } + +#define __cpu_to_be16(x) ((uint16_t)__swab16((x))) +#define __cpu_to_be32(x) ((uint32_t)__swab32((x))) + +#define __cpu_to_be16s(x) __swab16s((x)) +#define __cpu_to_be32s(x) __swab32s((x)) + + + +#define __le16_to_cpu(x) ((uint16_t)(x)) +#define __le32_to_cpu(x) ((uint32_t)(x)) + +#define __le32_to_cpus(x) { (void)(x); } +#define __le16_to_cpus(x) { (void)(x); } + +#define __be16_to_cpu(x) __swab16((uint16_t)(uint16_t)(x)) +#define __be32_to_cpu(x) __swab32((uint32_t)(uint32_t)(x)) + +#define __be16_to_cpus(x) __swab16s((x)) +#define __be32_to_cpus(x) __swab32s((x)) + +#endif /* __LITTLE_ENDIAN */ + + + +/** these are the conversion macros */ + +/** convert cpu order to little endian */ +#define cpu_to_le16 __cpu_to_le16 +#define cpu_to_le32 __cpu_to_le32 + +/** in-place convert cpu order to little endian */ +#define cpu_to_le16s __cpu_to_le16s +#define cpu_to_le32s __cpu_to_le32s + +/** convert cpu order to big endian */ +#define cpu_to_be16 __cpu_to_be16 +#define cpu_to_be32 __cpu_to_be32 + +/** in-place convert cpu order to big endian */ +#define cpu_to_be16s __cpu_to_be16s +#define cpu_to_be32s __cpu_to_be32s + + +/* same, but in reverse */ + +/** convert little endian to cpu order*/ +#define le16_to_cpu __le16_to_cpu +#define le32_to_cpu __le32_to_cpu + +/** in-place convert little endian to cpu order*/ +#define le16_to_cpus __le16_to_cpus +#define le32_to_cpus __le32_to_cpus + +/** convert big endian to cpu order*/ +#define be16_to_cpu __be16_to_cpu +#define be32_to_cpu __be32_to_cpu + +/** in-place convert big endian to cpu order*/ +#define be16_to_cpus __be16_to_cpus +#define be32_to_cpus __be32_to_cpus + + + +#endif /* BYTEORDER_H */ diff --git a/include/cmp_data_types.h b/include/cmp_data_types.h new file mode 100644 index 0000000..345c0ae --- /dev/null +++ b/include/cmp_data_types.h @@ -0,0 +1,190 @@ +/** + * @file cmp_data_types.h + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at), + * @date 2020 + * @brief definition of the different data types + * + * @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. + * + * + * Three data rates: + * fast cadence (nominally 25s) + * short cadence (nominally 50s) + * long cadence (nominally 600s) + * + * The science products are identified as this: + * FX = normal light flux + * NCOB = normal center of brightness + * EFX = extended flux + * ECOB = extended center of brightness + * The prefixes F, S and L stand for Fast, Short and Long cadence + */ + +#ifndef CMP_DATA_TYPE_H +#define CMP_DATA_TYPE_H + +#include <stdint.h> + +#define MODE_RAW_S_FX 100 +#define MODE_MODEL_ZERO_S_FX 101 +#define MODE_DIFF_ZERO_S_FX 102 +#define MODE_MODEL_MULTI_S_FX 103 +#define MODE_DIFF_MULTI_S_FX 104 + +#define MODE_MODEL_ZERO_S_FX_EFX 110 +#define MODE_DIFF_ZERO_S_FX_EFX 111 +#define MODE_MODEL_MULTI_S_FX_EFX 112 +#define MODE_DIFF_MULTI_S_FX_EFX 113 + +#define MODE_MODEL_ZERO_S_FX_NCOB 120 +#define MODE_DIFF_ZERO_S_FX_NCOB 121 +#define MODE_MODEL_MULTI_S_FX_NCOB 122 +#define MODE_DIFF_MULTI_S_FX_NCOB 123 + +#define MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB 130 +#define MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB 131 +#define MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB 132 +#define MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB 133 + +#define MODE_MODEL_ZERO_F_FX 140 +#define MODE_DIFF_ZERO_F_FX 141 +#define MODE_MODEL_MULTI_F_FX 142 +#define MODE_DIFF_MULTI_F_FX 143 + +#define MODE_MODEL_ZERO_F_FX_EFX 150 +#define MODE_DIFF_ZERO_F_FX_EFX 151 +#define MODE_MODEL_MULTI_F_FX_EFX 152 +#define MODE_DIFF_MULTI_F_FX_EFX 153 + +#define MODE_MODEL_ZERO_F_FX_NCOB 160 +#define MODE_DIFF_ZERO_F_FX_NCOB 161 +#define MODE_MODEL_MULTI_F_FX_NCOB 162 +#define MODE_DIFF_MULTI_F_FX_NCOB 163 + +#define MODE_MODEL_ZERO_F_FX_EFX_NCOB_ECOB 170 +#define MODE_DIFF_ZERO_F_FX_EFX_NCOB_ECOB 171 +#define MODE_MODEL_MULTI_F_FX_EFX_NCOB_ECOB 172 +#define MODE_DIFF_MULTI_F_FX_EFX_NCOB_ECOB 173 + +#define MODE_RAW_32 200 +#define MODE_DIFF_ZERO_32 201 +#define MODE_DIFF_MULTI_32 202 +#define MODE_MODEL_ZERO_32 203 +#define MODE_MODEL_MULTI_32 204 + +int lossy_rounding_16(uint16_t *data_buf, unsigned int samples, unsigned int + round); +int de_lossy_rounding_16(uint16_t *data_buf, uint32_t samples_used, uint32_t + round_used); + +int lossy_rounding_32(uint32_t *data_buf, unsigned int samples, unsigned int + round); +int de_lossy_rounding_32(uint32_t *data_buf, uint32_t samples_used, uint32_t + round_used); + +/* @see PLATO-LESIA-PL-RP-0031 Issue: 1.9 (N-DPU->ICU data rate) */ +struct __attribute__((packed)) S_FX { + uint8_t EXPOSURE_FLAGS; + uint32_t FX; +}; + +struct S_FX sub_S_FX(struct S_FX a, struct S_FX b); +struct S_FX add_S_FX(struct S_FX a, struct S_FX b); +int lossy_rounding_S_FX(struct S_FX *data_buf, unsigned int samples, + unsigned int round); +int de_lossy_rounding_S_FX(struct S_FX *data_buf, unsigned int samples_used, + unsigned int round_used); +struct S_FX cal_up_model_S_FX(struct S_FX data_buf, struct S_FX model_buf, + unsigned int model_value); + + +struct S_FX_EFX { + uint8_t EXPOSURE_FLAGS; + uint32_t FX; + uint32_t EFX; +}__attribute__((packed)); + +struct S_FX_EFX sub_S_FX_EFX(struct S_FX_EFX a, struct S_FX_EFX b); +struct S_FX_EFX add_S_FX_EFX(struct S_FX_EFX a, struct S_FX_EFX b); +int lossy_rounding_S_FX_EFX(struct S_FX_EFX *data, unsigned int samples, + unsigned int round); +int de_lossy_rounding_S_FX_EFX(struct S_FX_EFX *data_buf, unsigned int + samples_used, unsigned int round_used); +struct S_FX_EFX cal_up_model_S_FX_EFX(struct S_FX_EFX data_buf, struct S_FX_EFX + model_buf, unsigned int model_value); + + +struct S_FX_NCOB { + uint8_t EXPOSURE_FLAGS; + uint32_t FX; + uint32_t NCOB_X; + uint32_t NCOB_Y; +}__attribute__((packed)); + +struct S_FX_NCOB sub_S_FX_NCOB(struct S_FX_NCOB a, struct S_FX_NCOB b); +struct S_FX_NCOB add_S_FX_NCOB(struct S_FX_NCOB a, struct S_FX_NCOB b); +int lossy_rounding_S_FX_NCOB(struct S_FX_NCOB *data_buf, unsigned int samples, + unsigned int round); +int de_lossy_rounding_S_FX_NCOB(struct S_FX_NCOB *data_buf, unsigned int + samples_used, unsigned int round_used); +struct S_FX_NCOB cal_up_model_S_FX_NCOB(struct S_FX_NCOB data_buf, struct + S_FX_NCOB model_buf, unsigned int + model_value); + + +struct S_FX_EFX_NCOB_ECOB { + uint8_t EXPOSURE_FLAGS; + uint32_t FX; + uint32_t NCOB_X; + uint32_t NCOB_Y; + uint32_t EFX; + uint32_t ECOB_X; + uint32_t ECOB_Y; +}__attribute__((packed)); + +struct S_FX_EFX_NCOB_ECOB sub_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB a, + struct S_FX_EFX_NCOB_ECOB b); +struct S_FX_EFX_NCOB_ECOB add_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB a, + struct S_FX_EFX_NCOB_ECOB b); +int lossy_rounding_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + unsigned int samples, unsigned int round); +int de_lossy_rounding_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + unsigned int samples, + unsigned int round); + +struct F_FX { + uint32_t FX; +}__attribute__((packed)); + + +struct F_FX_EFX { + uint32_t FX; + uint32_t EFX; +}__attribute__((packed)); + + +struct F_FX_NCOB { + uint32_t FX; + uint32_t NCOB_X; + uint32_t NCOB_Y; +}__attribute__((packed)); + + +struct F_FX_EFX_NCOB_ECOB { + uint32_t FX; + uint32_t NCOB_X; + uint32_t NCOB_Y; + uint32_t EFX; + uint32_t ECOB_X; + uint32_t ECOB_Y; +}__attribute__((packed)); + +#endif /* CMP_DATA_TYPE_H */ diff --git a/include/cmp_debug.h b/include/cmp_debug.h new file mode 100644 index 0000000..ba345d2 --- /dev/null +++ b/include/cmp_debug.h @@ -0,0 +1,34 @@ +/** + * @file cmp_debug.h + * @author Dominik Loidolt (dominik.loidolt@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 compression/decompression debugging defines + */ + +#ifndef CMP_DEBUG_H +#define CMP_DEBUG_H + +#include <stdio.h> + +#if defined(DEBUG) || DDEBUGLEVEL > 0 + __extension__ + #define debug_print(...) \ + do { fprintf(stderr, __VA_ARGS__); } while (0) +#else + #define debug_print(...) \ + do {} while (0) +#endif + + +#endif /* CMP_DEBUG_H */ diff --git a/include/cmp_icu.h b/include/cmp_icu.h new file mode 100644 index 0000000..a2acd62 --- /dev/null +++ b/include/cmp_icu.h @@ -0,0 +1,28 @@ +/** + * @file cmp_icu.h + * @author Dominik Loidolt (dominik.loidolt@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 software compression library + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + +#ifndef _CMP_ICU_H_ +#define _CMP_ICU_H_ + +#include "../include/cmp_support.h" + +int icu_compress_data(struct cmp_cfg *cfg, struct cmp_info *info); + +#endif /* _CMP_ICU_H_ */ diff --git a/include/cmp_rdcu.h b/include/cmp_rdcu.h new file mode 100644 index 0000000..ee5cffb --- /dev/null +++ b/include/cmp_rdcu.h @@ -0,0 +1,43 @@ +/** + * @file cmp_rdcu.h + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at), + * @date 2019 + * + * @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 hardware compressor control library + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + + +#ifndef _CMP_RDCU_H_ +#define _CMP_RDCU_H_ + +#include <stdint.h> +#include "../include/cmp_support.h" + + +int rdcu_compress_data(const struct cmp_cfg *cfg); + +int rdcu_read_cmp_status(struct cmp_status *status); + +int rdcu_read_cmp_info(struct cmp_info *info); + +int rdcu_read_cmp_bitstream(const struct cmp_info *info, void *output_buf); + +int rdcu_read_model(const struct cmp_info *info, void *model_buf); + +int rdcu_interrupt_compression(void); + +void rdcu_enable_interrput_signal(void); +void rdcu_disable_interrput_signal(void); + +#endif /* _CMP_RDCU_H_ */ diff --git a/include/cmp_rdcu_extended.h b/include/cmp_rdcu_extended.h new file mode 100644 index 0000000..a4f33e0 --- /dev/null +++ b/include/cmp_rdcu_extended.h @@ -0,0 +1,30 @@ +/** + * @file cmp_rdcu_extended.h + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at), + * @date 2021 + * + * @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 _CMP_RDCU_EXTENDED_H_ +#define _CMP_RDCU_EXTENDED_H_ + +#include "../include/cmp_rdcu.h" + +int rdcu_start_compression(void); + +int rdcu_set_compression_register(const struct cmp_cfg *cfg); + +int rdcu_compress_data_parallel(const struct cmp_cfg *cfg, + const struct cmp_info *last_info); + +#endif /* _CMP_RDCU_EXTENDED_H_ */ diff --git a/include/cmp_support.h b/include/cmp_support.h new file mode 100644 index 0000000..62dd1e6 --- /dev/null +++ b/include/cmp_support.h @@ -0,0 +1,184 @@ +/** + * @file cmp_support.h + * @author Dominik Loidolt (dominik.loidolt@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 compression/decompression helper functions + */ + +#ifndef CMP_SUPPORT_H +#define CMP_SUPPORT_H + +#include <stdint.h> +#include <stddef.h> + + +#define GOLOMB_PAR_EXPOSURE_FLAGS 1 + +/* Compression Error Register bits definition, see RDCU-FRS-FN-0952 */ +#define SMALL_BUFFER_ERR_BIT 0x00 /* The length for the compressed data buffer is too small */ +#define CMP_MODE_ERR_BIT 0x01 /* The cmp_mode parameter is not set correctly */ +#define MODEL_VALUE_ERR_BIT 0x02 /* The model_value parameter is not set correctly */ +#define CMP_PAR_ERR_BIT 0x03 /* The spill, golomb_par combination is not set correctly */ +#define AP1_CMP_PAR_ERR_BIT 0x04 /* The ap1_spill, ap1_golomb_par combination is not set correctly (only HW compression) */ +#define AP2_CMP_PAR_ERR_BIT 0x05 /* The ap2_spill, ap2_golomb_par combination is not set correctly (only HW compression) */ +#define MB_ERR_BIT 0x06 /* Multi bit error detected by the memory controller (only HW compression) */ +#define SLAVE_BUSY_ERR_BIT 0x07 /* The bus master has received the "slave busy" status (only HW compression) */ + +#define MODE_RAW 0 +#define MODE_MODEL_ZERO 1 +#define MODE_DIFF_ZERO 2 +#define MODE_MODEL_MULTI 3 +#define MODE_DIFF_MULTI 4 + +#define MAX_MODEL_VALUE \ + 16UL /* the maximal model values used in the update equation for the new model */ + +/* valid compression parameter ranges for RDCU compression according to PLATO-UVIE-PL-UM-0001 */ +#define MAX_RDCU_CMP_MODE 4UL +#define MIN_RDCU_GOLOMB_PAR 1UL +#define MAX_RDCU_GOLOMB_PAR 63UL +#define MIN_RDCU_SPILL 2UL +#define MAX_RDCU_ROUND 2UL +/* for maximum spill value look at get_max_spill function */ + +/* valid compression parameter ranges for ICU compression */ +#define MIN_ICU_GOLOMB_PAR 1UL +#define MAX_ICU_GOLOMB_PAR UINT32_MAX +#define MIN_ICU_SPILL 2UL +/* for maximum spill value look at get_max_spill function */ +#define MAX_ICU_ROUND 3UL + +#define SAM2BYT \ + 2 /* sample to byte conversion factor; one samples has 16 bits (2 bytes) */ + +/** + * @brief The cmp_cfg structure can contain the complete configuration of the HW as + * well as the SW compressor. + * @note when using the 1d-differentiating mode or the raw mode (cmp_error = + * 0,2,4), the model parameters (model_value, model_buf, rdcu_model_adr, + * rdcu_new_model_adr) are ignored + * @note the icu_output_buf will not be used for HW compression + * @note the rdcu_***_adr parameters are ignored for SW compression + * @note semi adaptive compression not supported for SW compression; + * configuration parameters ap1\_golomb\_par, ap2\_golomb\_par, ap1\_spill, + * ap2\_spill will be ignored; + */ + +struct cmp_cfg { + uint32_t cmp_mode; /* 0: raw mode + * 1: model mode with zero escape symbol mechanism + * 2: 1d differencing mode without input model with zero escape symbol mechanism + * 3: model mode with multi escape symbol mechanism + * 4: 1d differencing mode without input model multi escape symbol mechanism + */ + uint32_t golomb_par; /* Golomb parameter for dictionary selection */ + uint32_t spill; /* Spillover threshold for encoding outliers */ + uint32_t model_value; /* Model weighting parameter */ + uint32_t round; /* Number of noise bits to be rounded */ + uint32_t ap1_golomb_par; /* Adaptive 1 spillover threshold; HW only */ + uint32_t ap1_spill; /* Adaptive 1 Golomb parameter; HW only */ + uint32_t ap2_golomb_par; /* Adaptive 2 spillover threshold; HW only */ + uint32_t ap2_spill; /* Adaptive 2 Golomb parameter; HW only */ + void *input_buf; /* Pointer to the data to compress buffer */ + uint32_t rdcu_data_adr; /* RDCU data to compress start address, the first data address in the RDCU SRAM; HW only */ + void *model_buf; /* Pointer to the model buffer */ + uint32_t rdcu_model_adr; /* RDCU model start address, the first model address in the RDCU SRAM */ + void *icu_new_model_buf; /* Pointer to the updated model buffer */ + uint32_t rdcu_new_model_adr;/* RDCU updated model start address, the address in the RDCU SRAM where the updated model is stored*/ + uint32_t samples; /* Number of samples (16 bit value) to compress, length of the data and model buffer */ + void *icu_output_buf; /* Pointer to the compressed data buffer (not used for RDCU compression) */ + uint32_t rdcu_buffer_adr; /* RDCU compressed data start address, the first output data address in the RDCU SRAM */ + uint32_t buffer_length; /* Length of the compressed data buffer in number of samples (16 bit values)*/ +}; + + +extern const struct cmp_cfg DEFAULT_CFG_MODEL; + +extern const struct cmp_cfg DEFAULT_CFG_DIFF; + + +/** + * @brief The cmp_status structure can contain the information of the + * compressor status register from the RDCU, see RDCU-FRS-FN-0632, + * but can also be used for the SW compression. + */ + +struct cmp_status { + uint8_t cmp_ready; /* Data Compressor Ready; 0: Compressor is busy 1: Compressor is ready */ + uint8_t cmp_active; /* Data Compressor Active; 0: Compressor is on hold; 1: Compressor is active */ + uint8_t data_valid; /* Compressor Data Valid; 0: Data is invalid; 1: Data is valid */ + uint8_t cmp_interrupted; /* Data Compressor Interrupted; HW only; 0: No compressor interruption; 1: Compressor was interrupted */ + uint8_t rdcu_interrupt_en; /* RDCU Interrupt Enable; HW only; 0: Interrupt is disabled; 1: Interrupt is enabled */ +}; + + +/** + * @brief The cmp_info structure can contain the information and metadata of an + * executed compression of the HW as well as the SW compressor. + * + * @note if SW compression is used the parameters rdcu_model_adr_used, rdcu_cmp_adr_used, + * ap1_cmp_size, ap2_cmp_size are not used and are therefore set to zero + */ + +struct cmp_info { + uint32_t cmp_mode_used; /* Compression mode used */ + uint8_t model_value_used; /* Model weighting parameter used */ + uint8_t round_used; /* Number of noise bits to be rounded used */ + uint32_t spill_used; /* Spillover threshold used */ + uint32_t golomb_par_used; /* Golomb parameter used */ + uint32_t samples_used; /* Number of samples (16 bit value) to be stored */ + uint32_t cmp_size; /* Compressed data size; measured in bits */ + uint32_t ap1_cmp_size; /* Adaptive compressed data size 1; measured in bits */ + uint32_t ap2_cmp_size; /* Adaptive compressed data size 2; measured in bits */ + uint32_t rdcu_new_model_adr_used; /* Updated model start address used */ + uint32_t rdcu_cmp_adr_used; /* Compressed data start address */ + uint16_t cmp_err; /* Compressor errors + * [bit 0] small_buffer_err; The length for the compressed data buffer is too small + * [bit 1] cmp_mode_err; The cmp_mode parameter is not set correctly + * [bit 2] model_value_err; The model_value parameter is not set correctly + * [bit 3] cmp_par_err; The spill, golomb_par combination is not set correctly + * [bit 4] ap1_cmp_par_err; The ap1_spill, ap1_golomb_par combination is not set correctly (only HW compression) + * [bit 5] ap2_cmp_par_err; The ap2_spill, ap2_golomb_par combination is not set correctly (only HW compression) + * [bit 6] mb_err; Multi bit error detected by the memory controller (only HW compression) + * [bit 7] slave_busy_err; The bus master has received the "slave busy" status (only HW compression) + * [bit 8] slave_blocked_err; The bus master has received the “slave blocked” status (only HW compression) + * [bit 9] invalid address_err; The bus master has received the “invalid address” status (only HW compression) */ +}; + +int is_a_pow_of_2(unsigned int v); +int ilog_2(uint32_t x); + +int model_mode_is_used(unsigned int cmp_mode); +int diff_mode_is_used(unsigned int cmp_mode); +int raw_mode_is_used(unsigned int cmp_mode); +int rdcu_supported_mode_is_used(unsigned int cmp_mode); + +int zero_escape_mech_is_used(unsigned int cmp_mode); +int multi_escape_mech_is_used(unsigned int cmp_mode); + +unsigned int round_fwd(unsigned int value, unsigned int round); +unsigned int round_inv(unsigned int value, unsigned int round); +unsigned int cal_up_model(unsigned int data, unsigned int model, unsigned int + model_value); + +uint32_t get_max_spill(unsigned int golomb_par, unsigned int cmp_mode); + +size_t size_of_a_sample(unsigned int cmp_mode); +unsigned int size_of_bitstream(unsigned int cmp_size); +unsigned int size_of_model(unsigned int samples, unsigned int cmp_mode); + +void print_cmp_cfg(const struct cmp_cfg *cfg); +void print_cmp_info(const struct cmp_info *info); + +#endif /* CMP_SUPPORT_H */ diff --git a/include/decmp.h b/include/decmp.h new file mode 100644 index 0000000..139273f --- /dev/null +++ b/include/decmp.h @@ -0,0 +1,31 @@ +/** + * @file decmp.h + * @author Dominik Loidolt (dominik.loidolt@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 software decompression library + */ + +#ifndef DECMP_H_ +#define DECMP_H_ + +#include "../include/cmp_support.h" + +void *malloc_decompressed_data(const struct cmp_info *info); + +int decompress_data(const void *compressed_data, void *de_model_buf, + const struct cmp_info *info, void *decompressed_data); + +double get_compression_ratio(const struct cmp_info *info); + +#endif /* DECMP_H_ */ diff --git a/include/rdcu_cmd.h b/include/rdcu_cmd.h new file mode 100644 index 0000000..0e4b8f4 --- /dev/null +++ b/include/rdcu_cmd.h @@ -0,0 +1,162 @@ +/** + * @file rdcu_cmd.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. + * + * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.6 + */ +#ifndef _RDCU_CMD_H_ +#define _RDCU_CMD_H_ + +#include <stdint.h> + +/* readable RDCU register addresses (RDCU-FRS-FN-0284) */ +#define FPGA_VERSION 0x10000000UL +#define RDCU_STATUS 0x10000004UL +#define LVDS_CORE_STATUS 0x10000008UL +#define SPW_LINK_STATUS 0x1000000CUL +#define SPW_ERR_CNTRS 0x10000010UL +#define RMAP_LAST_ERR 0x10000014UL +#define RMAP_NO_REPLY_ERR_CNTRS 0x10000018UL +#define RMAP_PCKT_ERR_CNTRS 0x1000001CUL +#define ADC_VALUES_1 0x10000020UL +#define ADC_VALUES_2 0x10000024UL +#define ADC_VALUES_3 0x10000028UL +#define ADC_VALUES_4 0x1000002CUL +#define ADC_STATUS 0x10000030UL +/* spare: 0x10000034UL */ +#define COMPR_STATUS 0x10000038UL +/* spare: 0x1000003CUL */ + +/* writeable RDCU register addresses (RDCU-FRS-FN-0284) */ +#define RDCU_RESET 0x10000040UL +#define SPW_LINK_CTRL 0x10000044UL +#define LVDS_CTRL 0x10000048UL +#define CORE_CTRL 0x1000004CUL +#define ADC_CTRL 0x10000050UL +/* spare: 0x10000054UL */ +#define COMPR_CTRL 0x10000058UL +/* spare: 0x1000005CUL */ + + + +/* writeable Data Compressor register addresses (RDCU-FRS-FN-0288) */ +#define COMPR_PARAM_1 0x11000000UL +#define COMPR_PARAM_2 0x11000004UL +#define ADAPTIVE_PARAM_1 0x11000008UL +#define ADAPTIVE_PARAM_2 0x1100000CUL +#define DATA_START_ADDR 0x11000010UL +#define MODEL_START_ADDR 0x11000014UL +#define NUM_SAMPLES 0x11000018UL +#define UPDATED_MODEL_START_ADDR 0x1100001CUL +#define COMPR_DATA_BUF_START_ADDR 0x11000020UL +#define COMPR_DATA_BUF_LEN 0x11000024UL +/* spare: 0x11000028UL */ +/* spare: 0x1100002CUL */ + +/* readable Data Compressor register addresses (RDCU-FRS-FN-0288) */ +#define USED_COMPR_PARAM_1 0x11000030UL +#define USED_COMPR_PARAM_2 0x11000034UL +#define COMPR_DATA_START_ADDR 0x11000038UL +#define COMPR_DATA_SIZE 0x1100003CUL +#define COMPR_DATA_ADAPTIVE_1_SIZE 0x11000040UL +#define COMPR_DATA_ADAPTIVE_2_SIZE 0x11000044UL +#define COMPR_ERROR 0x11000048UL +#define USED_UPDATED_MODEL_START_ADDR 0x1100004CUL +#define USED_NUMBER_OF_SAMPLES 0x11000050UL +/* spare: 0x11000054UL */ +/* spare: 0x11000058UL */ +/* spare: 0x1100005CUL */ + + +/* writeable SRAM EDAC register addresses (RDCU-FRS-FN-0292) */ +#define SRAM_EDAC_CTRL 0x01000000UL +/* spare: 0x01000004UL */ + +/* readable SRAM EDAC register addresses (RDCU-FRS-FN-0292) */ +#define SRAM_EDAC_STATUS 0x01000008UL +/* spare: 0x0100000CUL */ + +/* SRAM address range (RDCU-FRS-FN-0280) */ +#define RDCU_SRAM_START 0x00000000UL +#define RDCU_SRAM_END 0x007FFFFFUL +#define RDCU_SRAM_SIZE (RDCU_SRAM_END - RDCU_SRAM_START + 1UL) + + + + +int rdcu_read_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr); +int rdcu_write_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr); + +int rdcu_write_cmd_data(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size); +int rdcu_read_cmd_data(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size); + + +/* RDCU read accessors */ +int rdcu_read_cmd_fpga_version(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_rdcu_status(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_lvds_core_status(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_spw_link_status(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_spw_err_cntrs(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_rmap_last_err(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_rmap_no_reply_err_cntrs(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_rmap_pckt_err_cntrs(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_adc_values_1(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_adc_values_2(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_adc_values_3(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_adc_values_4(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_adc_status(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_status(uint16_t trans_id, uint8_t *cmd); + +/* RDCU read accessors */ +int rdcu_write_cmd_rdcu_reset(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_spw_link_ctrl(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_lvds_ctrl(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_core_ctrl(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_adc_ctrl(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_compr_ctrl(uint16_t trans_id, uint8_t *cmd); + +/* Data Compressor write accessors */ +int rdcu_write_cmd_compressor_param1(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_compressor_param2(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_adaptive_param1(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_adaptive_param2(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_data_start_addr(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_model_start_addr(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_num_samples(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_new_model_start_addr(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_compr_data_buf_start_addr(uint16_t trans_id, uint8_t *cmd); +int rdcu_write_cmd_compr_data_buf_len(uint16_t trans_id, uint8_t *cmd); + +/* Data Compressor read accessors */ +int rdcu_read_cmd_used_param1(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_used_param2(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_data_start_addr(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_data_size(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_data_adaptive_1_size(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_data_adaptive_2_size(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_compr_error(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_new_model_addr_used(uint16_t trans_id, uint8_t *cmd); +int rdcu_read_cmd_samples_used(uint16_t trans_id, uint8_t *cmd); + + +/* SRAM EDAC read accessors */ +int rdcu_read_cmd_sram_edac_status(uint16_t trans_id, uint8_t *cmd); + +/* SRAM EDAC write accessors */ +int rdcu_write_cmd_sram_edac_ctrl(uint16_t trans_id, uint8_t *cmd); + + +#endif /* _RDCU_CMD_H_ */ diff --git a/include/rdcu_ctrl.h b/include/rdcu_ctrl.h new file mode 100644 index 0000000..ec5b9a2 --- /dev/null +++ b/include/rdcu_ctrl.h @@ -0,0 +1,284 @@ +/** + * @file rdcu_ctrl.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. + * + * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.6 + */ +#ifndef _RDCU_CTRL_H_ +#define _RDCU_CTRL_H_ + +#include <stdint.h> + + +struct rdcu_mirror { + /* RDCU registers */ + uint32_t fpga_version; /* RDCU-FRS-FN-0522 */ + uint32_t rdcu_status; /* RDCU-FRS-FN-0532 */ + uint32_t lvds_core_status; /* RDCU-FRS-FN-0542 */ + uint32_t spw_link_status; /* RDCU-FRS-FN-0552 */ + uint32_t spw_err_cntrs; /* RDCU-FRS-FN-0562 */ + uint32_t rmap_last_err; /* RDCU-FRS-FN-0572 */ + uint32_t rmap_no_reply_err_cntrs; /* RDCU-FRS-FN-0582 */ + uint32_t rmap_pckt_err_cntrs; /* RDCU-FRS-FN-0592 */ + uint32_t adc_values_1; /* RDCU-FRS-FN-0602 */ + uint32_t adc_values_2; + uint32_t adc_values_3; + uint32_t adc_values_4; + uint32_t adc_status; /* RDCU-FRS-FN-0610 */ + uint32_t compr_status; /* RDCU-FRS-FN-0632 */ + uint32_t rdcu_reset; /* RDCU-FRS-FN-0662 */ + uint32_t spw_link_ctrl; /* RDCU-FRS-FN-0672 */ + uint32_t lvds_ctrl; /* RDCU-FRS-FN-0682 */ + uint32_t core_ctrl; /* RDCU-FRS-FN-0692 */ + uint32_t adc_ctrl; /* RDCU-FRS-FN-0712 */ + uint32_t compr_ctrl; /* RDCU-FRS-FN-0732 */ + + /* Data Compressor registers */ + uint32_t compressor_param1; /* RDCU-FRS-FN-0772 */ + uint32_t compressor_param2; /* RDCU-FRS-FN-0782 */ + uint32_t adaptive_param1; /* RDCU-FRS-FN-0792 */ + uint32_t adaptive_param2; /* RDCU-FRS-FN-0802 */ + uint32_t data_start_addr; /* RDCU-FRS-FN-0812 */ + uint32_t model_start_addr; /* RDCU-FRS-FN-0822 */ + uint32_t num_samples; /* RDCU-FRS-FN-0832 */ + uint32_t new_model_start_addr; /* RDCU-FRS-FN-0842 */ + uint32_t compr_data_buf_start_addr; /* RDCU-FRS-FN-0852 */ + uint32_t compr_data_buf_len; /* RDCU-FRS-FN-0862 */ + + uint32_t used_param1; /* RDCU-FRS-FN-0892 */ + uint32_t used_param2; /* RDCU-FRS-FN-0902 */ + uint32_t compr_data_start_addr; /* RDCU-FRS-FN-0912 */ + uint32_t compr_data_size; /* RDCU-FRS-FN-0922 */ + uint32_t compr_data_adaptive_1_size; /* RDCU-FRS-FN-0932 */ + uint32_t compr_data_adaptive_2_size; /* RDCU-FRS-FN-0942 */ + uint32_t compr_error; /* RDCU-FRS-FN-0952 */ + uint32_t new_model_addr_used; /* RDCU-FRS-FN-0962 */ + uint32_t samples_used; /* RDCU-FRS-FN-0970 */ + + + /* SRAM EDAC registers */ + uint32_t sram_edac_ctrl; /* RDCU-FRS-FN-1012 */ + uint32_t sram_edac_status; /* RDCU-FRS-FN-1032 */ + + uint8_t *sram; /* RDCU-FRS-FN-0280 */ +}; + + +/* RDCU registers */ +int rdcu_sync_fpga_version(void); +int rdcu_sync_rdcu_status(void); +int rdcu_sync_lvds_core_status(void); +int rdcu_sync_spw_link_status(void); +int rdcu_sync_spw_err_cntrs(void); +int rdcu_sync_rmap_last_err(void); +int rdcu_sync_rmap_no_reply_err_cntrs(void); +int rdcu_sync_rmap_pckt_err_cntrs(void); +int rdcu_sync_adc_values(int id); +int rdcu_sync_adc_status(void); +int rdcu_sync_compr_status(void); +int rdcu_sync_rdcu_reset(void); +int rdcu_sync_spw_link_ctrl(void); +int rdcu_sync_lvds_ctrl(void); +int rdcu_sync_core_ctrl(void); +int rdcu_sync_adc_ctrl(void); +int rdcu_sync_compr_ctrl(void); + + +/* Data Compressor registers */ +int rdcu_sync_compressor_param1(void); +int rdcu_sync_compressor_param2(void); +int rdcu_sync_adaptive_param1(void); +int rdcu_sync_adaptive_param2(void); +int rdcu_sync_data_start_addr(void); +int rdcu_sync_model_start_addr(void); +int rdcu_sync_num_samples(void); +int rdcu_sync_new_model_start_addr(void); +int rdcu_sync_compr_data_buf_start_addr(void); +int rdcu_sync_compr_data_buf_len(void); + +int rdcu_sync_used_param1(void); +int rdcu_sync_used_param2(void); +int rdcu_sync_compr_data_start_addr(void); +int rdcu_sync_compr_data_size(void); +int rdcu_sync_compr_data_adaptive_1_size(void); +int rdcu_sync_compr_data_adaptive_2_size(void); +int rdcu_sync_compr_error(void); +int rdcu_sync_new_model_addr_used(void); +int rdcu_sync_samples_used(void); + +/* SRAM EDAC registers */ +int rdcu_sync_sram_edac_ctrl(void); +int rdcu_sync_sram_edac_status(void); + +/* SRAM */ +int rdcu_sync_mirror_to_sram(uint32_t addr, uint32_t size, uint32_t mtu); +int rdcu_sync_sram_to_mirror(uint32_t addr, uint32_t size, uint32_t mtu); +int rdcu_sync_sram_mirror_parallel(uint32_t rx_addr, uint32_t rx_size, + uint32_t tx_addr, uint32_t tx_size, + uint32_t mtu); + +/* RDCU registers */ +uint16_t rdcu_get_fpga_version(void); + +uint32_t rdcu_get_rdcu_status_board_serial_number(void); +uint32_t rdcu_get_rdcu_status_fpga_core_power_good(void); +uint32_t rdcu_get_rdcu_status_io_power_good(void); +uint32_t rdcu_get_rdcu_status_reset_by_register(void); +uint32_t rdcu_get_rdcu_status_power_on_reset(void); + +uint8_t rdcu_get_rmap_target_logical_address(void); +uint8_t rdcu_get_rmap_target_cmd_key(void); +uint32_t rdcu_get_lvds_link_enabled(uint32_t link); + +uint16_t rdcu_get_spw_empty_pckt_cnt(void); +uint8_t rdcu_get_spw_run_clk_div(void); +uint8_t rdcu_get_spw_lnk_run_state(void); + +uint8_t rdcu_get_spw_lnk_credit_errs(void); +uint8_t rdcu_get_spw_lnk_escape_errs(void); +uint8_t rdcu_get_spw_lnk_parity_errs(void); +uint8_t rdcu_get_spw_lnk_disconnect_errs(void); + +uint8_t rdcu_get_rmap_last_error_user_code(void); +uint8_t rdcu_get_rmap_last_error_standard_code(void); + +uint8_t rdcu_get_rmap_incomplete_hdrs(void); +uint8_t rdcu_get_rmap_recv_reply_pckts(void); +uint8_t rdcu_get_recv_non_rmap_pckts(void); + +uint8_t rdcu_get_rmap_pckt_errs(void); +uint8_t rdcu_get_rmap_oper_errs(void); +uint8_t rdcu_get_rmap_cmd_auth_errs(void); +uint8_t rdcu_get_rmap_hdr_errs(void); + +uint16_t rdcu_get_adc_value(int id); + +uint32_t rdcu_get_valid_adc_values(void); +uint32_t rdcu_get_adc_logic_reset(void); +uint32_t rdcu_get_adc_logic_enabled(void); + + +uint32_t rdcu_get_rdcu_interrupt_enabled(void); +uint32_t rdcu_get_compr_status_valid(void); +uint32_t rdcu_get_data_compr_ready(void); +uint32_t rdcu_get_data_compr_interrupted(void); +uint32_t rdcu_get_data_compr_active(void); + + +void rdcu_set_rdcu_board_reset_keyword(uint8_t key); +void rdcu_set_rdcu_bus_reset(void); +void rdcu_clear_rdcu_bus_reset(void); +void rdcu_set_rdcu_rmap_error_cntr_reset(void); +void rdcu_clear_rdcu_rmap_error_cntr_reset(void); +void rdcu_set_rdcu_spw_error_cntr_reset(void); +void rdcu_clear_rdcu_spw_error_cntr_reset(void); +void rdcu_set_rdcu_board_reset(void); +void rdcu_clear_rdcu_board_reset(void); + +int rdcu_set_spw_link_run_clkdiv(uint8_t div); + +int rdcu_set_lvds_link_enabled(uint32_t link); +int rdcu_set_lvds_link_disabled(uint32_t link); + + +void rdcu_set_rmap_target_logical_address(uint8_t addr); +void rdcu_set_rmap_target_cmd_key(uint8_t key); + +void rdcu_set_adc_logic_reset(void); +void rdcu_clear_adc_logic_reset(void); +void rdcu_set_adc_logic_enabled(void); +void rdcu_set_adc_logic_disabled(void); + +void rdcu_set_rdcu_interrupt(void); +void rdcu_clear_rdcu_interrupt(void); +void rdcu_set_data_compr_interrupt(void); +void rdcu_clear_data_compr_interrupt(void); +void rdcu_set_data_compr_start(void); +void rdcu_clear_data_compr_start(void); + + + +/* Data Compressor registers */ +int rdcu_set_noise_bits_rounded(uint32_t rpar); +int rdcu_set_weighting_param(uint32_t mval); +int rdcu_set_compression_mode(uint32_t cmode); + +int rdcu_set_spillover_threshold(uint32_t spill); +int rdcu_set_golomb_param(uint32_t gpar); + +int rdcu_set_adaptive_1_spillover_threshold(uint32_t spill); +int rdcu_set_adaptive_1_golomb_param(uint32_t gpar); + +int rdcu_set_adaptive_2_spillover_threshold(uint32_t spill); +int rdcu_set_adaptive_2_golomb_param(uint32_t gpar); + +int rdcu_set_data_start_addr(uint32_t addr); + +int rdcu_set_model_start_addr(uint32_t addr); + +int rdcu_set_num_samples(uint32_t samples); + +int rdcu_set_new_model_start_addr(uint32_t addr); + +int rdcu_set_compr_data_buf_start_addr(uint32_t addr); + +int rdcu_set_compr_data_buf_len(uint32_t samples); + + +uint32_t rdcu_get_compression_mode(void); +uint32_t rdcu_get_noise_bits_rounded(void); +uint32_t rdcu_get_weighting_param(void); + +uint32_t rdcu_get_spillover_threshold(void); +uint32_t rdcu_get_golomb_param(void); + +uint32_t rdcu_get_compr_data_start_addr(void); + +uint32_t rdcu_get_compr_data_size(void); + +uint32_t rdcu_get_compr_data_adaptive_1_size(void); + +uint32_t rdcu_get_compr_data_adaptive_2_size(void); + +uint16_t rdcu_get_compr_error(void); + +uint32_t rdcu_get_new_model_addr_used(void); + +uint32_t rdcu_get_samples_used(void); + +/* SRAM EDAC registers */ +int rdcu_edac_set_sub_chip_die_addr(uint32_t ca); +void rdcu_edac_set_ctrl_reg_read_op(void); +void rdcu_edac_set_ctrl_reg_write_op(void); +void rdcu_edac_set_bypass(void); +void rdcu_edac_clear_bypass(void); +void rdcu_edac_set_scrub_info(uint8_t nfo); + + +uint32_t rdcu_edac_get_sub_chip_die_addr(void); +uint32_t rdcu_edac_get_bypass_status(void); +uint8_t rdcu_edac_get_scrub_info(void); + +/* SRAM */ +int rdcu_read_sram(void *buf, uint32_t addr, uint32_t size); +int rdcu_write_sram(void *buf, uint32_t addr, uint32_t size); +int rdcu_write_sram_8(uint8_t *buf, uint32_t addr, uint32_t size); +int rdcu_write_sram_16(uint16_t *buf, uint32_t addr, uint32_t size); +int rdcu_write_sram_32(uint32_t *buf, uint32_t addr, uint32_t size); + + +int rdcu_ctrl_init(void); + + +#endif /* _RDCU_CTRL_H_ */ diff --git a/include/rdcu_pkt_to_file.h b/include/rdcu_pkt_to_file.h new file mode 100644 index 0000000..87c3d4b --- /dev/null +++ b/include/rdcu_pkt_to_file.h @@ -0,0 +1,39 @@ +/** + * @file rdcu_pkt_to_file.h + * @author Dominik Loidolt (dominik.loidolt@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. + * + */ + +#ifndef _RDCU_PKT_TO_FILE_H_ +#define _RDCU_PKT_TO_FILE_H_ + +#include "cmp_support.h" + +/* directory where the tc files are stored, when --rdcu_pkt option is set */ +#define TC_DIR "TC_FILES" + +#define RDCU_DEST_KEY 0x0 + +#define MAX_TC_FOLDER_DIR_LEN 256 + +int init_rmap_pkt_to_file(void); + +void set_tc_folder_dir(const char *dir_name); + +int gen_write_rdcu_pkts(const struct cmp_cfg *cfg); +int gen_read_rdcu_pkts(const struct cmp_info *info); +int gen_rdcu_parallel_pkts(const struct cmp_cfg *cfg, + const struct cmp_info *last_info); + +#endif /* _RDCU_PKT_TO_FILE_H_ */ diff --git a/include/rdcu_rmap.h b/include/rdcu_rmap.h new file mode 100644 index 0000000..b6ce568 --- /dev/null +++ b/include/rdcu_rmap.h @@ -0,0 +1,65 @@ +/** + * @file rdcu_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 _RDCU_RMAP_H_ +#define _RDCU_RMAP_H_ + +#include <stdint.h> + + + +int rdcu_submit_tx(const uint8_t *cmd, int cmd_size, + const uint8_t *data, int data_size); + + +int rdcu_gen_cmd(uint16_t trans_id, uint8_t *cmd, + uint8_t rmap_cmd_type, + uint32_t addr, uint32_t size); + +int rdcu_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd), + void *addr, int data_len); + +int rdcu_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 rdcu_package(uint8_t *blob, + const uint8_t *cmd, uint32_t cmd_size, + const uint8_t non_crc_bytes, + const uint8_t *data, uint32_t data_size); + +void rdcu_set_destination_logical_address(uint8_t addr); + +int rdcu_set_destination_path(uint8_t *path, uint8_t len); +int rdcu_set_return_path(uint8_t *path, uint8_t len); +void rdcu_set_source_logical_address(uint8_t addr); +void rdcu_set_destination_key(uint8_t key); +size_t rdcu_get_data_mtu(void); + +int rdcu_rmap_sync_status(void); + +void rdcu_rmap_reset_log(void); + +int rdcu_rmap_init(size_t 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 /* _RDCU_RMAP_H_ */ diff --git a/include/rmap.h b/include/rmap.h new file mode 100644 index 0000000..5156679 --- /dev/null +++ b/include/rmap.h @@ -0,0 +1,217 @@ +/** + * @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 hdr_crc; + uint8_t data_crc; +}; + + + +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, uint32_t len); +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/include/tool_lib.h b/include/tool_lib.h new file mode 100644 index 0000000..3592078 --- /dev/null +++ b/include/tool_lib.h @@ -0,0 +1,46 @@ +/** + * @file tool_lib.h + * @author Johannes Seelig (johannes.seelig@univie.ac.at) + * @author Dominik Loidolt (dominik.loidolt@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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "cmp_support.h" +#include "rdcu_pkt_to_file.h" + +#define PROGRAM_NAME "cmp_tool" +#define MAX_CONFIG_LINE 256 + +#define DEFAULT_OUTPUT_PREFIX "OUTPUT" + +void Print_Help(const char *argv); + +int read_cmp_cfg(const char *file_name, struct cmp_cfg *cfg, int verbose_en); +int read_cmp_info(const char *file_name, struct cmp_info *info, int verbose_en); + +uint16_t * read_file16(const char *file_name, uint32_t samples, int verbose_en); +uint32_t * read_file32(const char *file_name, uint32_t buf_len, int verbose_en); + +int write_cmp_data_file(const void *buf, uint32_t buf_size, const char + *output_prefix, const char *name_extension, int verbose); +int write_to_file16(const uint16_t *buf, uint32_t buf_len, const char + *output_prefix, const char *name_extension, int verbose); + +void print_cfg(const struct cmp_cfg *cfg, int rdcu_cfg); + +int write_info(const struct cmp_info *info, const char *output_prefix, + int machine_cfg); diff --git a/lib/cmp_data_types.c b/lib/cmp_data_types.c new file mode 100644 index 0000000..e6bbe92 --- /dev/null +++ b/lib/cmp_data_types.c @@ -0,0 +1,647 @@ +/** + * @file cmp_data_types.c + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at) + * @date 2020 + * @brief collection of functions to handle the different compression data types + * + * @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. + * + */ + + +#include <stddef.h> +#include <stdio.h> + +#include "../include/cmp_data_types.h" +#include "../include/cmp_support.h" +#include "../include/cmp_debug.h" + + +/** + * @brief rounding down the least significant digits of a uint16_t data buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * + * @param data_buf uint16_t formatted data buffer + * @param samples the size of the data buffer measured in uint16_t samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_16(uint16_t *data_buf, unsigned int samples, unsigned int + round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is + * necessary */ + if (round == 0) + return 0; + + for (i = 0; i < samples; i++) + data_buf[i] = round_fwd(data_buf[i], round); /* this is the lossy step */ + + return 0; +} + + +/** + * @brief rounding back the least significant digits of the data buffer + * + * @param data_buf pointer to the data to process + * @param samples_used the size of the data and model buffer in 16 bit units + * @param round_used used number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int de_lossy_rounding_16(uint16_t *data_buf, uint32_t samples_used, uint32_t + round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is necessary */ + if (round_used == 0) + return 0; + + for (i = 0; i < samples_used; i++) { + /* check if data are not to big for a overflow */ + uint16_t mask = (uint16_t)(~0 << (16-round_used)); + if (data_buf[i] & mask) { + debug_print("de_lossy_rounding_16 failed!\n"); + return -1; + } + data_buf[i] = round_inv(data_buf[i], round_used); + } + return 0; +} + + +/** + * @brief rounding down the least significant digits of a uint32_t data buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * + * @param data_buf a uint32_t formatted data buffer + * @param samples the size of the data buffer measured in uint16_t samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_32(uint32_t *data_buf, unsigned int samples, unsigned int + round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is + * necessary */ + if (round == 0) + return 0; + + for (i = 0; i < samples; i++) + data_buf[i] = round_fwd(data_buf[i], round); /* this is the lossy step */ + + return 0; +} + + +int de_lossy_rounding_32(uint32_t *data_buf, uint32_t samples_used, uint32_t + round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is necessary */ + if (round_used == 0) + return 0; + + for (i = 0; i < samples_used; i++) { + /* check if data are not to big for a overflow */ + uint32_t mask = (uint32_t)(~0 << (32-round_used)); + if (data_buf[i] & mask) { + debug_print("de_lossy_rounding_32 failed!\n"); + return -1; + } + data_buf[i] = round_inv(data_buf[i], round_used); + } + return 0; +} + + +struct S_FX sub_S_FX(struct S_FX a, struct S_FX b) +{ + struct S_FX result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS - b.EXPOSURE_FLAGS; + result.FX = a.FX - b.FX; + + return result; +} + + +struct S_FX add_S_FX(struct S_FX a, struct S_FX b) +{ + struct S_FX result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS + b.EXPOSURE_FLAGS; + result.FX = a.FX + b.FX; + + return result; +} + + +/** + * @brief rounding down the least significant digits of a S_FX data buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * @note the exposure_flags are not rounded + * + * @param data_buf a S_FX formatted data buffer + * @param samples the size of the data buffer measured in S_FX samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_S_FX(struct S_FX *data_buf, unsigned int samples, unsigned + int round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is + * necessary */ + if (round == 0) + return 0; + + for (i = 0; i < samples; i++) + data_buf[i].FX = round_fwd(data_buf[i].FX, round); + + return 0; +} + + +int de_lossy_rounding_S_FX(struct S_FX *data_buf, unsigned int samples_used, + unsigned int round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (round_used == 0) /* round 0 means loss less compression, no further processing is necessary */ + return 0; + + for (i = 0; i < samples_used; i++) { + uint32_t mask = (~0U << (32-round_used)); + + if (data_buf[i].FX & mask) { + debug_print("Errolr: de_lossy_rounding_S_FX failed\n"); + return -1; + } + + data_buf[i].FX = round_inv(data_buf[i].FX, round_used); + } + return 0; +} + + +struct S_FX cal_up_model_S_FX(struct S_FX data_buf, struct S_FX model_buf, + unsigned int model_value) +{ + struct S_FX result; + + result.EXPOSURE_FLAGS = (uint8_t)cal_up_model(data_buf.EXPOSURE_FLAGS, + model_buf.EXPOSURE_FLAGS, + model_value); + result.FX = cal_up_model(data_buf.FX, model_buf.FX, model_value); + + return result; +} + + +struct S_FX_EFX sub_S_FX_EFX(struct S_FX_EFX a, struct S_FX_EFX b) +{ + struct S_FX_EFX result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS - b.EXPOSURE_FLAGS; + result.FX = a.FX - b.FX; + result.EFX = a.EFX - b.EFX; + + return result; +} + + +struct S_FX_EFX add_S_FX_EFX(struct S_FX_EFX a, struct S_FX_EFX b) +{ + struct S_FX_EFX result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS + b.EXPOSURE_FLAGS; + result.FX = a.FX + b.FX; + result.EFX = a.EFX + b.EFX; + + return result; +} + + +/** + * @brief rounding down the least significant digits of a S_FX_EFX data buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * @note the exposure_flags are not rounded + * + * @param data_buf a S_FX_EFX formatted data buffer + * @param samples the size of the data buffer measured in S_FX_EFX samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_S_FX_EFX(struct S_FX_EFX *data, unsigned int samples, + unsigned int round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data) + return -1; + + /* round 0 means loss less compression, no further processing is + * necessary */ + if (round == 0) + return 0; + + for (i = 0; i < samples; i++) + { + data[i].FX = round_fwd(data[i].FX, round); + data[i].EFX = round_fwd(data[i].EFX, round); + } + return 0; +} + + +int de_lossy_rounding_S_FX_EFX(struct S_FX_EFX *data_buf, unsigned int + samples_used, unsigned int round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (round_used == 0) /* round 0 means loss less compression, no further processing is necessary */ + return 0; + + for (i = 0; i < samples_used; i++) { + uint32_t mask = (~0U << (32-round_used)); + + if (data_buf[i].FX & mask) { + debug_print("de_lossy_rounding_S_FX failed!\n"); + return -1; + } + if (data_buf[i].EFX & mask) { + debug_print("de_lossy_rounding_S_FX failed!\n"); + return -1; + } + + data_buf[i].FX = round_inv(data_buf[i].FX, round_used); + data_buf[i].EFX = round_inv(data_buf[i].EFX, round_used); + } + return 0; +} + + +struct S_FX_EFX cal_up_model_S_FX_EFX(struct S_FX_EFX data_buf, struct S_FX_EFX + model_buf, unsigned int model_value) +{ + struct S_FX_EFX result; + + result.EXPOSURE_FLAGS = + (uint8_t)cal_up_model(data_buf.EXPOSURE_FLAGS, + model_buf.EXPOSURE_FLAGS, model_value); + result.FX = cal_up_model(data_buf.FX, model_buf.FX, model_value); + result.EFX = cal_up_model(data_buf.EFX, model_buf.FX, model_value); + + return result; +} + + +struct S_FX_NCOB sub_S_FX_NCOB(struct S_FX_NCOB a, struct S_FX_NCOB b) +{ + struct S_FX_NCOB result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS - b.EXPOSURE_FLAGS; + result.FX = a.FX - b.FX; + result.NCOB_X = a.NCOB_X - b.NCOB_X; + result.NCOB_Y = a.NCOB_Y - b.NCOB_Y; + + return result; +} + + +struct S_FX_NCOB add_S_FX_NCOB(struct S_FX_NCOB a, struct S_FX_NCOB b) +{ + struct S_FX_NCOB result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS + b.EXPOSURE_FLAGS; + result.FX = a.FX + b.FX; + result.NCOB_X = a.NCOB_X + b.NCOB_X; + result.NCOB_Y = a.NCOB_Y + b.NCOB_Y; + + return result; +} + + +/** + * @brief rounding down the least significant digits of a S_FX_NCOB data buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * @note the exposure_flags are not rounded + * + * @param data_buf a S_FX_NCOB formatted data buffer + * @param samples the size of the data buffer measured in S_FX_NCOB samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_S_FX_NCOB(struct S_FX_NCOB *data_buf, unsigned int samples, + unsigned int round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data_buf) + return -1; + + /* round 0 means loss less compression, no further processing is + * necessary */ + if (round == 0) + return 0; + + for (i = 0; i < samples; i++) + { + data_buf[i].FX = round_fwd(data_buf[i].FX, round); + data_buf[i].NCOB_X = round_fwd(data_buf[i].NCOB_X, round); + data_buf[i].NCOB_Y = round_fwd(data_buf[i].NCOB_Y, round); + } + return 0; +} + + +int de_lossy_rounding_S_FX_NCOB(struct S_FX_NCOB *data_buf, unsigned int + samples_used, unsigned int round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (round_used == 0) /* round 0 means loss less compression, no further processing is necessary */ + return 0; + + for (i = 0; i < samples_used; i++) { + uint32_t mask = (~0U << (32-round_used)); + + if (data_buf[i].FX & mask) { + debug_print("de_lossy_rounding_S_FX_NCOB failed!\n"); + return -1; + } + if (data_buf[i].NCOB_X & mask) { + debug_print("de_lossy_rounding_S_FX_NCOB failed!\n"); + return -1; + } + if (data_buf[i].NCOB_Y & mask) { + debug_print("de_lossy_rounding_S_FX_NCOB failed!\n"); + return -1; + } + + data_buf[i].FX = round_inv(data_buf[i].FX, round_used); + data_buf[i].NCOB_X = round_inv(data_buf[i].NCOB_X, round_used); + data_buf[i].NCOB_Y = round_inv(data_buf[i].NCOB_Y, round_used); + } + return 0; +} + + +struct S_FX_NCOB cal_up_model_S_FX_NCOB(struct S_FX_NCOB data_buf, struct S_FX_NCOB + model_buf, unsigned int model_value) +{ + struct S_FX_NCOB result; + + result.EXPOSURE_FLAGS = + (uint8_t)cal_up_model(data_buf.EXPOSURE_FLAGS, + model_buf.EXPOSURE_FLAGS, model_value); + result.FX = cal_up_model(data_buf.FX, model_buf.FX, model_value); + result.NCOB_X = cal_up_model(data_buf.NCOB_X, model_buf.NCOB_X, model_value); + result.NCOB_Y = cal_up_model(data_buf.NCOB_Y, model_buf.NCOB_Y, model_value); + + return result; +} + + +struct S_FX_EFX_NCOB_ECOB sub_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB a, + struct S_FX_EFX_NCOB_ECOB b) +{ + struct S_FX_EFX_NCOB_ECOB result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS - b.EXPOSURE_FLAGS; + result.FX = a.FX - b.FX; + result.NCOB_X = a.NCOB_X - b.NCOB_X; + result.NCOB_Y = a.NCOB_Y - b.NCOB_Y; + result.EFX = a.EFX - b.EFX; + result.ECOB_X = a.ECOB_X - b.ECOB_X; + result.ECOB_Y = a.ECOB_Y - b.ECOB_Y; + + return result; +} + + +struct S_FX_EFX_NCOB_ECOB add_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB a, + struct S_FX_EFX_NCOB_ECOB b) +{ + struct S_FX_EFX_NCOB_ECOB result; + + result.EXPOSURE_FLAGS = a.EXPOSURE_FLAGS + b.EXPOSURE_FLAGS; + result.FX = a.FX + b.FX; + result.NCOB_X = a.NCOB_X + b.NCOB_X; + result.NCOB_Y = a.NCOB_Y + b.NCOB_Y; + result.EFX = a.EFX + b.EFX; + result.ECOB_X = a.ECOB_X + b.ECOB_X; + result.ECOB_Y = a.ECOB_Y + b.ECOB_Y; + + return result; +} + + +/** + * @brief rounding down the least significant digits of a S_FX_EFX_NCOB_ECOB data + * buffer + * + * @note this step involves data loss (if round > 0) + * @note change the data buffer in-place + * @note the exposure_flags are not rounded + * + * @param data_buf a S_FX_EFX_NCOB_ECOB formatted data buffer + * @param samples the size of the data buffer measured in + * S_FX_EFX_NCOB_ECOB samples + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int lossy_rounding_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + unsigned int samples, unsigned int round) +{ + size_t i; + + if (!samples) + return 0; + + if (!data_buf) + return -1; + + if (round == 0) /* round 0 means loss less compression, no further processing is necessary */ + return 0; + + for (i = 0; i < samples; i++) + { + data_buf[i].FX = round_fwd(data_buf[i].FX, round); + data_buf[i].NCOB_X = round_fwd(data_buf[i].NCOB_X, round); + data_buf[i].NCOB_Y = round_fwd(data_buf[i].NCOB_Y, round); + data_buf[i].EFX = round_fwd(data_buf[i].EFX, round); + data_buf[i].ECOB_X = round_fwd(data_buf[i].ECOB_X, round); + data_buf[i].ECOB_Y = round_fwd(data_buf[i].ECOB_Y, round); + } + return 0; +} + + +int de_lossy_rounding_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + unsigned int samples_used, + unsigned int round_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (round_used == 0) /* round 0 means loss less compression, no further processing is necessary */ + return 0; + + for (i = 0; i < samples_used; i++) + { + uint32_t mask = (~0U << (32-round_used)); + + if (data_buf[i].FX & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + if (data_buf[i].NCOB_X & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + if (data_buf[i].NCOB_Y & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + if (data_buf[i].EFX & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + if (data_buf[i].ECOB_X & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + if (data_buf[i].ECOB_Y & mask) { + debug_print("de_lossy_rounding_S_FX_EFX_NCOB_ECOB failed!\n"); + return -1; + } + + data_buf[i].FX = round_inv(data_buf[i].FX, round_used); + data_buf[i].NCOB_X = round_inv(data_buf[i].NCOB_X, round_used); + data_buf[i].NCOB_Y = round_inv(data_buf[i].NCOB_Y, round_used); + data_buf[i].EFX = round_inv(data_buf[i].EFX, round_used); + data_buf[i].ECOB_X = round_inv(data_buf[i].ECOB_X, round_used); + data_buf[i].ECOB_Y = round_inv(data_buf[i].ECOB_Y, round_used); + } + return 0; +} + + +struct S_FX_EFX_NCOB_ECOB cal_up_model_S_FX_EFX_NCOB_ECOB + (struct S_FX_EFX_NCOB_ECOB data_buf, struct S_FX_EFX_NCOB_ECOB + model_buf, unsigned int model_value) +{ + struct S_FX_EFX_NCOB_ECOB result; + + result.EXPOSURE_FLAGS = + (uint8_t)cal_up_model(data_buf.EXPOSURE_FLAGS, + model_buf.EXPOSURE_FLAGS, model_value); + result.FX = cal_up_model(data_buf.FX, model_buf.FX, model_value); + result.NCOB_X = cal_up_model(data_buf.NCOB_X, model_buf.NCOB_X, model_value); + result.NCOB_Y = cal_up_model(data_buf.NCOB_Y, model_buf.NCOB_Y, model_value); + result.EFX = cal_up_model(data_buf.EFX, model_buf.EFX, model_value); + result.ECOB_X = cal_up_model(data_buf.ECOB_X, model_buf.ECOB_X, model_value); + result.ECOB_Y = cal_up_model(data_buf.ECOB_Y, model_buf.ECOB_Y, model_value); + + return result; +} diff --git a/lib/cmp_icu.c b/lib/cmp_icu.c new file mode 100644 index 0000000..1a8d157 --- /dev/null +++ b/lib/cmp_icu.c @@ -0,0 +1,1781 @@ +/** + * @file icu_cmp.c + * @author Dominik Loidolt (dominik.loidolt@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 software compression library + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + + +#include <stdio.h> +#include <stdint.h> +#include <string.h> +#include <limits.h> + +#include "../include/cmp_support.h" +#include "../include/cmp_data_types.h" +#include "../include/cmp_icu.h" +#include "../include/byteorder.h" +#include "../include/cmp_debug.h" + + + +/** + * @brief check if the compressor configuration is valid for a SW compression, + * see the user manual for more information (PLATO-UVIE-PL-UM-0001). + * + * @param cfg configuration contains all parameters required for compression + * @param info compressor information contains information of an executed + * compression (can be NULL) + * + * @returns 0 when configuration is valid, invalid configuration otherwise + */ + +int icu_cmp_cfg_valid(const struct cmp_cfg *cfg, struct cmp_info *info) +{ + int cfg_invalid = 0; + + if (!cfg) { + debug_print("Error: compression configuration structure is NULL.\n"); + return -1; + } + + if (!info) + debug_print("Warning: compressor information structure is NULL.\n"); + + if (info) + info->cmp_err = 0; /* reset errors */ + + if (cfg->input_buf == NULL) { + debug_print("Error: The input_buf buffer for the data to be compressed is NULL.\n"); + cfg_invalid++; + } + + if (cfg->samples == 0) { + debug_print("Warning: The samples parameter is 0. No data are compressed. This behavior may not be intended.\n"); + } + + /* icu_output_buf can be NULL if rdcu compression is used */ + if (cfg->icu_output_buf == NULL) { + debug_print("Error: The icu_output_buf buffer for the compressed data is NULL.\n"); + cfg_invalid++; + } + + if (cfg->buffer_length == 0 && cfg->samples != 0) { + debug_print("Error: The buffer_length is set to 0. There is no space to store the compressed data.\n"); + cfg_invalid++; + } + + if (cfg->icu_output_buf == cfg->input_buf) { + debug_print("Error: The icu_output_buf buffer is the same as the input_buf buffer.\n"); + cfg_invalid++; + } + + if (model_mode_is_used(cfg->cmp_mode)) { + if (cfg->model_buf == NULL) { + debug_print("Error: The model_buf buffer for the model data is NULL.\n"); + cfg_invalid++; + } + + if (cfg->model_buf == cfg->input_buf) { + debug_print("Error: The model_buf buffer is the same as the input_buf buffer.\n"); + cfg_invalid++; + } + + if (cfg->model_buf == cfg->icu_output_buf) { + debug_print("Error: The model_buf buffer is the same as the icu_output_buf buffer.\n"); + cfg_invalid++; + } + + if (cfg->icu_new_model_buf == cfg->input_buf) { + debug_print("Error: The icu_new_model_buf buffer is the same as the input_buf buffer.\n"); + cfg_invalid++; + } + + if (cfg->icu_new_model_buf == cfg->icu_output_buf) { + debug_print("Error: The icu_output_buf buffer is the same as the icu_output_buf buffer.\n"); + cfg_invalid++; + } + } + + if (raw_mode_is_used(cfg->cmp_mode)) { + if (cfg->samples > cfg->buffer_length) { + debug_print("Error: The buffer_length is to small to hold the data form the input_buf.\n"); + cfg_invalid++; + } + } else { + if (cfg->samples*size_of_a_sample(cfg->cmp_mode) < + cfg->buffer_length*sizeof(uint16_t)/3) /* TODO: have samples and buffer_lengt the same unit */ + debug_print("Warning: The size of the icu_output_buf is 3 times smaller than the input_buf. This is probably unintentional.\n"); + } + + + if (!(diff_mode_is_used(cfg->cmp_mode) + || model_mode_is_used(cfg->cmp_mode) + || raw_mode_is_used(cfg->cmp_mode))) { + debug_print("Error: selected cmp_mode: %u is not supported\n.", + cfg->cmp_mode); + if (info) + info->cmp_err |= 1UL << CMP_MODE_ERR_BIT; + cfg_invalid++; + } + + if (raw_mode_is_used(cfg->cmp_mode)) /* additional checks are not needed for the raw mode */ + return -cfg_invalid; + + if (model_mode_is_used(cfg->cmp_mode)) { + if (cfg->model_value > MAX_MODEL_VALUE) { + debug_print("Error: selected model_value: %u is invalid. Largest supported value is: %lu.\n", + cfg->model_value, MAX_MODEL_VALUE); + if (info) + info->cmp_err |= 1UL << MODEL_VALUE_ERR_BIT; + cfg_invalid++; + } + } + + if (cfg->golomb_par < MIN_ICU_GOLOMB_PAR || + cfg->golomb_par > MAX_ICU_GOLOMB_PAR) { + debug_print("Error: The selected Golomb parameter: %u is not supported. The Golomb parameter has to be between [%lu, %u].\n", + cfg->golomb_par, MIN_ICU_GOLOMB_PAR, MAX_ICU_GOLOMB_PAR); + if (info) + info->cmp_err |= 1UL << CMP_PAR_ERR_BIT; + cfg_invalid++; + } + + if (cfg->spill < MIN_ICU_SPILL) { + debug_print("Error: The selected spillover threshold value: %u is too small. Smallest possible spillover value is: %lu.\n", + cfg->spill, MIN_ICU_SPILL); + if (info) + info->cmp_err |= 1UL << CMP_PAR_ERR_BIT; + cfg_invalid++; + } + + if (cfg->spill > get_max_spill(cfg->golomb_par, cfg->cmp_mode)) { + debug_print("Error: The selected spillover threshold value: %u is too large for the selected Golomb parameter: %u, the largest possible spillover value in the selected compression mode is: %u.\n", + cfg->spill, cfg->golomb_par, + get_max_spill(cfg->golomb_par, cfg->cmp_mode)); + if (info) + info->cmp_err |= 1UL << CMP_PAR_ERR_BIT; + cfg_invalid++; + } + +#ifdef ADAPTIVE_CHECK_ENA + /* + * ap1_spill and ap2_spill are not used for the icu_compression + */ + + if (cfg->ap1_spill > get_max_spill(cfg->ap1_golomb_par, cfg->cmp_mode)) { + if (info) + info->cmp_err |= 1UL << AP1_CMP_PAR_ERR_BIT; + cfg_invalid++; + } + + if (cfg->ap2_spill > get_max_spill(cfg->ap2_golomb_par, cfg->cmp_mode)) { + if (info) + info->cmp_err |= 1UL << AP2_CMP_PAR_ERR_BIT; + cfg_invalid++; + } +#endif + + if (cfg->round > MAX_ICU_ROUND) { + debug_print("Error: selected round parameter: %u is not supported. Largest supported value is: %lu.\n", + cfg->round, MAX_ICU_ROUND); + cfg_invalid++; + } + + return -(cfg_invalid); +} + + +/** + * @brief sets the compression information used based on the compression + * configuration + * + * @param cfg compression configuration struct + * @param info compressor information struct to set the used compression + * parameters (can be NULL) + * + * @note set cmp_size, ap1_cmp_size, ap2_cmp_size will be set to 0 + * + * @returns 0 on success, error otherwise + */ + + +static int set_info(struct cmp_cfg *cfg, struct cmp_info *info) +{ + if (!cfg) + return -1; + + if (cfg->cmp_mode > UINT8_MAX) + return -1; + + if (cfg->round > UINT8_MAX) + return -1; + + if (cfg->model_value > UINT8_MAX) + return -1; + + if(info) { + info->cmp_err = 0; + info->cmp_mode_used = (uint8_t)cfg->cmp_mode; + info->model_value_used = (uint8_t)cfg->model_value; + info->round_used = (uint8_t)cfg->round; + info->spill_used = cfg->spill; + info->golomb_par_used = cfg->golomb_par; + info->samples_used = cfg->samples; + info->cmp_size = 0; + info->ap1_cmp_size = 0; + info->ap2_cmp_size = 0; + info->rdcu_new_model_adr_used = cfg->rdcu_new_model_adr; + info->rdcu_cmp_adr_used = cfg->rdcu_buffer_adr; + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and rounding of a uint16_t data buffer + * + * @note change the data_buf in-place + * @note output is I[0] = I[0], I[i] = I[i] - I[i-1], where i is 1,2,..samples-1 + * + * @param data_buf pointer to the uint16_t formatted data buffer to process + * @param samples amount of data samples in the data buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_16(uint16_t *data_buf, unsigned int samples, unsigned int round) +{ + size_t i; + + if (!data_buf) + return -1; + + lossy_rounding_16(data_buf, samples, round); + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data_buf[i] = data_buf[i] - data_buf[i-1]; + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and rounding of a uint32_t data buffer + * + * @note change the data_buf in-place + * @note output is I_0 = I_0, I_i = I_i - I_i-1, where i is the array index + * + * @param data_buf pointer to the uint32_t formatted data buffer to process + * @param samples amount of data samples in the data_buf buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_32(uint32_t *data_buf, unsigned int samples, unsigned int round) +{ + size_t i; + + if (!data_buf) + return -1; + + lossy_rounding_32(data_buf, samples, round); + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data_buf[i] = data_buf[i] - data_buf[i-1]; + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and round of a S_FX data buffer + * + * @note change the data_buf in-place + * @note output is I_0 = I_0, I_i = I_i - I_i-1, where i is the array index + * + * @param data_buf pointer to a S_FX data buffer + * @param samples amount of data samples in the data buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_S_FX(struct S_FX *data, unsigned int samples, unsigned int + round) +{ + size_t i; + int err; + + if (!data) + return -1; + + err = lossy_rounding_S_FX(data, samples, round); + if (err) + return err; + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data[i] = sub_S_FX(data[i], data[i-1]); + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and rounding of a S_FX_EFX data buffer + * + * @note change the data_buf in-place + * @note output is I_0 = I_0, I_i = I_i - I_i-1, where i is the array index + * + * @param data_buf pointer to a S_FX_EFX data buffer + * @param samples amount of data samples in the data buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_S_FX_EFX(struct S_FX_EFX *data, unsigned int samples, unsigned + int round) +{ + size_t i; + int err; + + if (!data) + return -1; + + err = lossy_rounding_S_FX_EFX(data, samples, round); + if (err) + return err; + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data[i] = sub_S_FX_EFX(data[i], data[i-1]); + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and rounding of a S_FX_NCOB data buffer + * + * @note change the data_buf in-place + * @note output is I_0 = I_0, I_i = I_i - I_i-1, where i is the array index + * + * @param data_buf pointer to a S_FX_NCOB data buffer + * @param samples amount of data samples in the data buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_S_FX_NCOB(struct S_FX_NCOB *data, unsigned int samples, unsigned + int round) +{ + size_t i; + int err; + + if (!data) + return -1; + + err = lossy_rounding_S_FX_NCOB(data, samples, round); + if (err) + return err; + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data[i] = sub_S_FX_NCOB(data[i], data[i-1]); + } + return 0; +} + + +/** + * @brief 1d-differentiating pre-processing and rounding of a S_FX_EFX_NCOB_ECOB data buffer + * + * @note change the data_buf in-place + * @note output is I_0 = I_0, I_i = I_i - I_i-1, where i is the array index + * + * @param data_buf pointer to a S_FX_EFX_NCOB_ECOB data buffer + * @param samples amount of data samples in the data buffer + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int diff_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data, unsigned int + samples, unsigned int round) +{ + size_t i; + int err; + + if (!data) + return -1; + + err = lossy_rounding_S_FX_EFX_NCOB_ECOB(data, samples, round); + if (err) + return err; + + for (i = samples - 1; i > 0; i--) { + /* possible underflow is intended */ + data[i] = sub_S_FX_EFX_NCOB_ECOB(data[i], data[i-1]); + } + + return 0; +} + + +/** + * @brief model pre-processing and rounding of a uint16_t data buffer + * + * @note overwrite the data_buf in-place with the result + * @note update the model_buf in-place if up_model_buf = NULL + * + * @param data_buf pointer to the uint16_t data buffer to process + * @param model_buf pointer to the model buffer of the data to process + * @param up_model_buf pointer to the updated model buffer can be NULL + * @param samples amount of data samples in the data_buf and model_buf buffer + * @param model_value model weighting parameter + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int model_16(uint16_t *data_buf, uint16_t *model_buf, uint16_t *up_model_buf, + unsigned int samples, unsigned int model_value, unsigned int round) +{ + size_t i; + + if (!data_buf) + return -1; + + if (!model_buf) + return -1; + + if (model_value > MAX_MODEL_VALUE) + return -1; + + if (!up_model_buf) + up_model_buf = model_buf; + + for (i = 0; i < samples; i++) { + uint16_t round_input = (uint16_t)round_fwd(data_buf[i], round); + uint16_t round_model = (uint16_t)round_fwd(model_buf[i], round); + /* possible underflow is intended */ + data_buf[i] = round_input - round_model; /* TDOO: check if this is the right order */ + /* round back input because for decompression the accurate data + * are not available + */ + up_model_buf[i] = (uint16_t)cal_up_model(round_inv(round_input, round), + model_buf[i], model_value); + } + return 0; +} + + +/** + * @brief model pre-processing and round_input of a uint32_t data buffer + * + * @note overwrite the data_buf in-place with the result + * @note update the model_buf in-place if up_model_buf = NULL + * + * @param data_buf pointer to the uint32_t data buffer to process + * @param model_buf pointer to the model buffer of the data to process + * @param samples amount of data samples in the data_buf and model_buf buffer + * @param model_value model weighting parameter + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int model_32(uint32_t *data_buf, uint32_t *model_buf, unsigned int samples, + unsigned int model_value, unsigned int round) +{ + size_t i; + + if (!data_buf) + return -1; + + if (!model_buf) + return -1; + + if (model_value > MAX_MODEL_VALUE) + return -1; + + for (i = 0; i < samples; i++) { + uint32_t round_input = round_fwd(data_buf[i], round); + uint32_t round_model = round_fwd(model_buf[i], round); + /* possible underflow is intended */ + data_buf[i] = round_input - round_model; + /* round back input because for decompression the accurate data + * are not available + */ + model_buf[i] = cal_up_model(round_inv(round_input, round), + model_buf[i], model_value); + } + return 0; +} + + +/** + * @brief model pre-processing and round_input of a S_FX data buffer + * + * @note overwrite the data_buf in-place with the result + * @note update the model_buf in-place if up_model_buf = NULL + * + * @param data_buf pointer to the S_FX data buffer to process + * @param model_buf pointer to the model buffer of the data to process + * @param model_buf pointer to the updated model buffer (if NULL model_buf + * will be overwrite with the updated model) + * @param samples amount of data samples in the data_buf and model_buf buffer + * @param model_value model weighting parameter + * @param round number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +int model_S_FX(struct S_FX *data_buf, struct S_FX *model_buf, + struct S_FX *up_model_buf, unsigned int samples, + unsigned int model_value, unsigned int round) +{ + size_t i; + + if (!samples) + return 0; + + if (!model_buf) + return -1; + + if (model_value > MAX_MODEL_VALUE) + return -1; + + if (!up_model_buf) /* overwrite the model buffer if no up_model_buf is set */ + up_model_buf = model_buf; + + + for (i = 0; i < samples; i++) { + struct S_FX round_data = data_buf[i]; + struct S_FX round_model = model_buf[i]; + int err; + + err = lossy_rounding_S_FX(&round_data, 1, round); + if (err) + return err; + + err = lossy_rounding_S_FX(&round_model, 1, round); + if (err) + return err; + + /* possible underflow is intended */ + data_buf[i] = sub_S_FX(round_data, round_model); + + /* round back input because for decompression the accurate data + * are not available + */ + err = de_lossy_rounding_S_FX(&round_data, 1, round); + if (err) + return err; + up_model_buf[i] = cal_up_model_S_FX(round_data, model_buf[i], + model_value); + } + + return 0; +} + + +/** + * @brief data pre-processing to decorrelate the data + * + * @param cfg configuration contains all parameters required for compression + * + * @returns 0 on success, error otherwise + */ + +int pre_process(struct cmp_cfg *cfg) +{ + if (!cfg) + return -1; + + if (cfg->samples == 0) + return 0; + + if (!cfg->input_buf) + return -1; + + switch (cfg->cmp_mode) { + case MODE_RAW: + case MODE_RAW_S_FX: + return 0; /* in raw mode no pre-processing is necessary */ + break; + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + return model_16((uint16_t *)cfg->input_buf, (uint16_t *)cfg->model_buf, + (uint16_t *)cfg->icu_new_model_buf, cfg->samples, + cfg->model_value, cfg->round); + break; + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + return diff_16((uint16_t *)cfg->input_buf, cfg->samples, + cfg->round); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + return model_S_FX((struct S_FX *)cfg->input_buf, (struct S_FX *)cfg->model_buf, + (struct S_FX *)cfg->icu_new_model_buf, cfg->samples, + cfg->model_value, cfg->round); + break; + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + return diff_S_FX((struct S_FX *)cfg->input_buf, cfg->samples, cfg->round); + break; + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + return diff_S_FX_EFX((struct S_FX_EFX *)cfg->input_buf, + cfg->samples, cfg->round); + break; + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + return diff_S_FX_NCOB((struct S_FX_NCOB *)cfg->input_buf, + cfg->samples, cfg->round); + break; + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + return diff_S_FX_EFX_NCOB_ECOB((struct S_FX_EFX_NCOB_ECOB *)cfg->input_buf, + cfg->samples, cfg->round); + break; + case MODE_MODEL_ZERO_32: + case MODE_MODEL_MULTI_32: + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + return model_32((uint32_t *)cfg->input_buf, (uint32_t *)cfg->model_buf, + cfg->samples, cfg->model_value, cfg->round); + break; + case MODE_DIFF_ZERO_32: + case MODE_DIFF_MULTI_32: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + return diff_32((uint32_t *)cfg->input_buf, cfg->samples, cfg->round); + break; + default: + debug_print("Error: Compression mode not supported.\n"); + } + + return -1; +} + + +static uint8_t map_to_pos_alg_8(int8_t value_to_map) +{ + if (value_to_map < 0) + /* NOTE: possible integer overflow is intended */ + return (uint8_t)((-value_to_map) * 2 - 1); + else + /* NOTE: possible integer overflow is intended */ + return (uint8_t)(value_to_map * 2); +} + + +static uint16_t map_to_pos_alg_16(int16_t value_to_map) +{ + if (value_to_map < 0) + /* NOTE: possible integer overflow is intended */ + return (uint16_t)((-value_to_map) * 2 - 1); + else + /* NOTE: possible integer overflow is intended */ + return (uint16_t)(value_to_map * 2); +} + + +static uint32_t map_to_pos_alg_32(int32_t value_to_map) +{ + if (value_to_map < 0) + /* NOTE: possible integer overflow is intended */ + return (uint32_t)((-value_to_map) * 2 - 1); + else + /* NOTE: possible integer overflow is intended */ + return (uint32_t)(value_to_map * 2); +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a 16 bit buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the uint16_t data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos_16(uint16_t *data_buf, uint32_t samples, int zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i] = map_to_pos_alg_16(data_buf[i]); + if (zero_mode_used) + data_buf[i] += 1; + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a 32 bit buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the uint32_t data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos_32(uint32_t *data_buf, uint32_t samples, int + zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i] = map_to_pos_alg_32(data_buf[i]); + if (zero_mode_used) + data_buf[i] += 1; + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a S_FX buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the S_FX data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +int map_to_pos_S_FX(struct S_FX *data_buf, uint32_t samples, int + zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i].EXPOSURE_FLAGS = + map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = map_to_pos_alg_32(data_buf[i].FX); + + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS += 1; */ + data_buf[i].FX += 1; + } + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a S_FX_EFX buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the S_FX_EFX data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos_S_FX_EFX(struct S_FX_EFX *data_buf, uint32_t samples, int + zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i].EXPOSURE_FLAGS = + map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].EFX = map_to_pos_alg_32(data_buf[i].EFX); + + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS += 1; */ + data_buf[i].FX += 1; + data_buf[i].EFX += 1; + } + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a S_FX_NCOB buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the S_FX_NCOB data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos_S_FX_NCOB(struct S_FX_NCOB *data_buf, uint32_t samples, + int zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i].EXPOSURE_FLAGS = + map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].NCOB_X = map_to_pos_alg_32(data_buf[i].NCOB_X); + data_buf[i].NCOB_Y = map_to_pos_alg_32(data_buf[i].NCOB_Y); + + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS += 1; */ + data_buf[i].FX += 1; + data_buf[i].NCOB_X += 1; + data_buf[i].NCOB_Y += 1; + } + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range for a S_FX_EFX_NCOB_ECOB buffer + * + * @note overwrite the data_buf in-place with the result + * + * @param data_buf pointer to the S_FX_EFX_NCOB_ECOB data buffer to process + * @param samples amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + uint32_t samples, int zero_mode_used) +{ + size_t i; + + if (!data_buf) + return -1; + + for (i = 0; i < samples; i++) { + data_buf[i].EXPOSURE_FLAGS = + map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].NCOB_X = map_to_pos_alg_32(data_buf[i].NCOB_X); + data_buf[i].NCOB_Y = map_to_pos_alg_32(data_buf[i].NCOB_Y); + data_buf[i].EFX = map_to_pos_alg_32(data_buf[i].EFX); + data_buf[i].ECOB_X = map_to_pos_alg_32(data_buf[i].ECOB_X); + data_buf[i].ECOB_Y = map_to_pos_alg_32(data_buf[i].ECOB_Y); + + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS += 1; */ + data_buf[i].FX += 1; + data_buf[i].NCOB_X += 1; + data_buf[i].NCOB_Y += 1; + data_buf[i].EFX += 1; + data_buf[i].ECOB_X += 1; + data_buf[i].ECOB_Y += 1; + } + } + return 0; +} + + +/** + * @brief map the signed output of the pre-processing stage to a unsigned value + * range + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the data to process + * @param buf_len length of the data to process + * + * @returns 0 on success, error otherwise + */ + +static int map_to_pos(struct cmp_cfg *cfg) +{ + int zero_mode_used; + + if (!cfg) + return -1; + + if (cfg->samples == 0) + return 0; + + if (!cfg->input_buf) + return -1; + + zero_mode_used = zero_escape_mech_is_used(cfg->cmp_mode); + + switch (cfg->cmp_mode) { + case MODE_RAW: + case MODE_RAW_S_FX: + return 0; /* in raw mode no mapping is necessary */ + break; + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + return map_to_pos_16((uint16_t *)cfg->input_buf, cfg->samples, + zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + return map_to_pos_S_FX((struct S_FX *)cfg->input_buf, + cfg->samples, zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + return map_to_pos_S_FX_EFX((struct S_FX_EFX *)cfg->input_buf, + cfg->samples, zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + return map_to_pos_S_FX_NCOB((struct S_FX_NCOB *)cfg->input_buf, + cfg->samples, zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + return map_to_pos_S_FX_EFX_NCOB_ECOB((struct S_FX_EFX_NCOB_ECOB *)cfg->input_buf, + cfg->samples, zero_mode_used); + break; + case MODE_MODEL_ZERO_32: + case MODE_MODEL_MULTI_32: + case MODE_DIFF_ZERO_32: + case MODE_DIFF_MULTI_32: + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + return map_to_pos_32((uint32_t *)cfg->input_buf, cfg->samples, + zero_mode_used); + break; + default: + debug_print("Error: Compression mode not supported.\n"); + break; + + } + + return -1; +} + + +/** + * @brief forms the codeword accurate to the Rice code and returns its length + * + * @param m Golomb parameter, only m's which are power of 2 and >0 + * are allowed! + * @param log2_m Rice parameter, is log_2(m) calculate outside function + * for better performance + * @param value value to be encoded + * @param cw address were the encode code word is stored + * + * @returns length of the encoded code word in bits + */ + +static unsigned int Rice_encoder(unsigned int value, unsigned int m, + unsigned int log2_m, unsigned int *cw) +{ + unsigned int g; /* quotient of value/m */ + unsigned int q; /* quotient code without ending zero */ + unsigned int r; /* remainder of value/m */ + unsigned int rl; /* remainder length */ + + g = value >> log2_m; /* quotient, number of leading bits */ + q = (1U << g) - 1; /* prepare the quotient code without ending zero */ + + r = value & (m-1); /* calculate the remainder */ + rl = log2_m + 1; /* length of the remainder (+1 for the 0 in the + * quotient code) + */ + *cw = (q << rl) | r; /* put the quotient and remainder code together */ + + return rl + g; /* calculate the length of the code word */ +} + + +/** + * @brief forms the codeword accurate to the Golomb code and returns its length + * + * @param m Golomb parameter, only m's which are power of 2 and >0 + * are allowed! + * @param log2_m is log_2(m) calculate outside function for better + * performance + * @param value value to be encoded + * @param cw address were the encode code word is stored + * + * @returns length of the encoded code word in bits + */ + +static unsigned int Golomb_encoder(unsigned int value, unsigned int m, + unsigned int log2_m, unsigned int *cw) +{ + unsigned int len0, b, g, q, lg; + unsigned int len; + unsigned int cutoff; + + len0 = log2_m + 1; /* codeword length in group 0 */ + cutoff = (1U << (log2_m+1)) - m; /* members in group 0 */ + if (cutoff == 0) /* for powers of two we fix cutoff = m */ + cutoff = m; + + if (value < cutoff) { /* group 0 */ + *cw = value; + len = len0; + } else { /* other groups */ + b = (cutoff << 1); /* form the base codeword */ + g = (value-cutoff)/m; /* this group is which one */ + lg = len0 + g; /* it has lg remainder bits */ + q = (1U << g) - 1; /* prepare the left side in unary */ + *cw = (q << (len0+1)) + b + (value-cutoff)-g*m; /* composed codeword */ + len = lg + 1; /* length of the codeword */ + } + return len; +} + + +typedef unsigned int (*encoder_ptr)(unsigned int, unsigned int, unsigned int, + unsigned int*); + +static encoder_ptr select_encoder(unsigned int golomb_par) +{ + if (!golomb_par) + return NULL; + + if (is_a_pow_of_2(golomb_par)) + return &Rice_encoder; + else + return &Golomb_encoder; +} + + +/** + * @brief safe (but slow) way to put the value of up to 32 bits into a + * bitstream accessed as 32-bit RAM in big endian + * @param value the value to put, it will be masked + * @param bitOffset bit index where the bits will be put, seen from the very + * beginning of the bitstream + * @param nBits number of bits to put in the bitstream + * @param destAddr this is the pointer to the beginning of the bitstream + * @param dest_len length of the bitstream buffer (starting at destAddr) + * @returns number of bits written, 0 if the number was too big, -1 if the + * destAddr buffer is to small to store the bitstream + * @note works in SRAM2 + */ + +static unsigned int put_n_bits32(unsigned int value, unsigned int bitOffset, + unsigned int nBits, unsigned int *destAddr, + unsigned int dest_len) +{ + unsigned int *localAddr; + unsigned int bitsLeft, shiftRight, shiftLeft, localEndPos; + unsigned int mask; + + /* check if destination buffer is large enough */ + /* TODO: adapt that to the other science products */ + if ((bitOffset + nBits) > (((dest_len+1)/2)*2 * 16)) { + debug_print("Error: The icu_output_buf buffer is not small to hold the compressed data.\n"); + return -2U; + } + + /* leave in case of erroneous input */ + if (nBits == 0) + return 0; + if (nBits > 32) + return 0; + + /* separate the bitOffset into word offset (set localAddr pointer) and local bit offset (bitsLeft) */ + localAddr = destAddr + (bitOffset >> 5); + bitsLeft = bitOffset & 0x1f; + + /* (M) we mask the value first to match its size in nBits */ + /* the calculations can be re-used in the unsegmented code, so we have no overhead */ + shiftRight = 32 - nBits; + mask = 0xffffffff >> shiftRight; + value &= mask; + + /* to see if we need to split the value over two words we need the right end position */ + localEndPos = bitsLeft + nBits; + + if (localEndPos <= 32) { + /* UNSEGMENTED + * + *|-----------|XXXXX|----------------| + * bitsLeft n bitsRight + * + * -> to get the mask: + * shiftRight = bitsLeft + bitsRight = 32 - n + * shiftLeft = bitsRight + * + */ + + /* shiftRight = 32 - nBits; */ /* see (M) above! */ + shiftLeft = shiftRight - bitsLeft; + + /* generate the mask, the bits for the values will be true */ + /* mask = (0xffffffff >> shiftRight) << shiftLeft; */ /* see (M) above! */ + mask <<= shiftLeft; + + /* clear the destination with inverse mask */ + *(localAddr) &= ~mask; + + /* assign the value */ + *(localAddr) |= (value << (32 - localEndPos)); /* NOTE: 32-localEndPos = shiftLeft can be simplified */ + + } else { + /* SEGMENTED + * + *|-----------------------------|XXX| |XX|------------------------------| + * bitsLeft n1 n2 bitsRight + * + * -> to get the mask part 1: + * shiftright = bitsleft + * n1 = n - (bitsleft + n - 32) = 32 - bitsleft + * + * -> to get the mask part 2: + * n2 = bitsleft + n - 32 + * shiftleft = 32 - n2 = 32 - (bitsleft + n - 32) = 64 - bitsleft - n + * + */ + + unsigned int n2 = bitsLeft + nBits - 32; + + /* part 1: */ + shiftRight = bitsLeft; + mask = 0xffffffff >> shiftRight; + + /* clear the destination with inverse mask */ + *(localAddr) &= ~mask; + + /* assign the value part 1 */ + *(localAddr) |= (value >> n2); + + /* part 2: */ + /* adjust address */ + localAddr += 1; + shiftLeft = 64 - bitsLeft - nBits; + mask = 0xffffffff << shiftLeft; + + /* clear the destination with inverse mask */ + *(localAddr) &= ~mask; + + /* assign the value part 2 */ + *(localAddr) |= (value << (32 - n2)); + } + return nBits; +} + + +struct encoder_struct { + encoder_ptr encoder; + unsigned int log2_golomb_par; /* pre-calculated for performance increase */ + uint32_t cmp_size; /* Compressed data size; measured in bits */ +}; + + +static int encode_raw(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + size_t cmp_size_in_bytes; + + if (!cfg) + return -1; + + if (!cfg->icu_output_buf) + return -1; + + if (!cfg->input_buf) + return -1; + + cmp_size_in_bytes = cfg->samples * size_of_a_sample(cfg->cmp_mode); + + enc->cmp_size = cmp_size_in_bytes * CHAR_BIT; /* cmp_size is measured in bits */ + + if (cmp_size_in_bytes > cfg->buffer_length * sizeof(uint16_t)) + return -1; + + memcpy(cfg->icu_output_buf, cfg->input_buf, cmp_size_in_bytes); + + return 0; +} + + +static int encode_raw_16(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + int err; + + err = encode_raw(cfg, enc); + if (err) + return err; + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + { + size_t i; + uint16_t *p = cfg->icu_output_buf; + + for (i = 0; i < cfg->samples; i++) + cpu_to_be16s(&p[i]); + + } +#endif /* __BYTE_ORDER__ */ + return 0; +} + + +static int encode_raw_S_FX(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + int err; + + err = encode_raw(cfg, enc); + if (err) + return err; + +#if defined(__LITTLE_ENDIAN) + { + size_t i; + for (i = 0; i < cfg->samples; i++) { + struct S_FX *output_buf = cfg->icu_output_buf; + output_buf[i].FX = cpu_to_be32(output_buf[i].FX); + } + } +#endif + return 0; +} + + +static int encode_normal(uint32_t value_to_encode, struct cmp_cfg *cfg, + struct encoder_struct *enc) +{ + unsigned int code_word; + unsigned int cw_len; + int err; + + if (!enc->encoder) + return -1; + + cw_len = enc->encoder(value_to_encode, cfg->golomb_par, + enc->log2_golomb_par, &code_word); + + err = put_n_bits32(code_word, enc->cmp_size, cw_len, + cfg->icu_output_buf, cfg->buffer_length); + if (err <= 0) + return err; + + enc->cmp_size += cw_len; + + return 0; +} + + +static int encode_outlier_zero(uint32_t value_to_encode, int max_bits, + struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + int err; + + if (max_bits > 32) + return -1; + + /* use zero as escape symbol */ + encode_normal(0, cfg, enc); + + /* put the data unencoded in the bitstream */ + err = put_n_bits32(value_to_encode, enc->cmp_size, max_bits, + cfg->icu_output_buf, cfg->buffer_length); + if (err <= 0) { + return err; + } + + enc->cmp_size += max_bits; + + return 0; +} + + +static int cal_multi_offset(unsigned int unencoded_data) +{ + if (unencoded_data <= 0x3) + return 0; + if (unencoded_data <= 0xF) + return 1; + if (unencoded_data <= 0x3F) + return 2; + if (unencoded_data <= 0xFF) + return 3; + if (unencoded_data <= 0x3FF) + return 4; + if (unencoded_data <= 0xFFF) + return 5; + if (unencoded_data <= 0x3FFF) + return 6; + if (unencoded_data <= 0xFFFF) + return 7; + if (unencoded_data <= 0x3FFFF) + return 8; + if (unencoded_data <= 0xFFFFF) + return 9; + if (unencoded_data <= 0x3FFFFF) + return 10; + if (unencoded_data <= 0xFFFFFF) + return 11; + if (unencoded_data <= 0x3FFFFFF) + return 12; + if (unencoded_data <= 0xFFFFFFF) + return 13; + if (unencoded_data <= 0x3FFFFFFF) + return 14; + else + return 15; +} + + +static int encode_outlier_multi(uint32_t value_to_encode, struct cmp_cfg *cfg, + struct encoder_struct *enc) +{ + uint32_t unencoded_data; + unsigned int unencoded_data_len; + uint32_t escape_sym; + int escape_sym_offset; + int err; + + /* + * In this mode we put the difference between the data and the spillover + * threshold value (unencoded_data) after a encoded escape symbol, which + * indicate that the next codeword is unencoded. + * We use different escape symbol depended on the size the needed bit of + * unencoded data: + * 0, 1, 2 bits needed for unencoded data -> escape symbol is spill + 0 + * 3, 4 bits needed for unencoded data -> escape symbol is spill + 1 + * .. + */ + + unencoded_data = value_to_encode - cfg->spill; + escape_sym_offset = cal_multi_offset(unencoded_data); + escape_sym = cfg->spill + escape_sym_offset; + unencoded_data_len = (escape_sym_offset + 1) * 2; + + /* put the escape symbol in the bitstream */ + encode_normal(escape_sym, cfg, enc); + + /* put the unencoded data in the bitstream */ + err = put_n_bits32(unencoded_data, enc->cmp_size, unencoded_data_len, + cfg->icu_output_buf, cfg->buffer_length); + if (err <= 0) + return err; + + enc->cmp_size += unencoded_data_len; + + return 0; +} + +static int encode_outlier(uint32_t value_to_encode, int bit_len, struct cmp_cfg + *cfg, struct encoder_struct *enc) +{ + if (multi_escape_mech_is_used(cfg->cmp_mode)) + return encode_outlier_multi(value_to_encode, cfg, enc); + + if (zero_escape_mech_is_used(cfg->cmp_mode)) + return encode_outlier_zero(value_to_encode, bit_len, cfg, enc); + + return -1; +} + + +int encode_value(uint32_t value_to_encode, int bit_len, struct cmp_cfg *cfg, + struct encoder_struct *enc) +{ + /* 0 is an outlier in case of a zero-escape mechanism, because an + * overflow can occur by incrementing by one + */ + if (value_to_encode >= cfg->spill || + (zero_escape_mech_is_used(cfg->cmp_mode) && value_to_encode == 0)) + return encode_outlier(value_to_encode, bit_len, cfg, enc); + else + return encode_normal(value_to_encode, cfg, enc); +} + + +static int encode_16(uint16_t *data_to_encode, struct cmp_cfg *cfg, + struct encoder_struct *enc) +{ + size_t i; + + if (!cfg) + return -1; + + if (!cfg->samples) + return 0; + + if (!data_to_encode) + return -1; + + if (!enc) + return -1; + + + for (i = 0; i < cfg->samples; i++) { + int err = encode_value(data_to_encode[i], 16, cfg, enc); + if (err) + return err; + } + return 0; +} + + +static int encode_32(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + uint32_t *data_to_encode = cfg->input_buf; + size_t i; + + for (i = 0; i < cfg->samples; i++) { + int err = encode_value(data_to_encode[i], 32, cfg, enc); + if (err) + return err; + } + return 0; +} + + +static int encode_S_FX(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + struct S_FX *data_to_encode = cfg->input_buf; + size_t i; + struct cmp_cfg cfg_exp_flag = *cfg; + struct encoder_struct enc_exp_flag = *enc; + + cfg_exp_flag.golomb_par = GOLOMB_PAR_EXPOSURE_FLAGS; + enc_exp_flag.log2_golomb_par = ilog_2(GOLOMB_PAR_EXPOSURE_FLAGS); + + for (i = 0; i < cfg->samples; i++) { + int err; + + /* err = encode_value(data_to_encode[i].EXPOSURE_FLAGS, 8, &cfg_exp_flag, enc); */ + err = encode_normal(data_to_encode[i].EXPOSURE_FLAGS, &cfg_exp_flag, &enc_exp_flag); + if (err) + return err; + enc->cmp_size = enc_exp_flag.cmp_size; + + enc->log2_golomb_par = ilog_2(cfg->golomb_par); + err = encode_value(data_to_encode[i].FX, 32, cfg, enc); + if (err) + return err; + + enc_exp_flag.cmp_size = enc->cmp_size; + } + + return 0; +} + + +static int encode_S_FX_EFX(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + struct S_FX_EFX *data_to_encode = cfg->input_buf; + size_t i; + + for (i = 0; i < cfg->samples; i++) { + int err; + + err = encode_value(data_to_encode[i].EXPOSURE_FLAGS, 8, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].FX, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].EFX, 32, cfg, enc); + if (err) + return err; + } + return 0; +} + + +static int encode_S_FX_NCOB(struct cmp_cfg *cfg, struct encoder_struct *enc) +{ + struct S_FX_NCOB *data_to_encode = cfg->input_buf; + size_t i; + + for (i = 0; i < cfg->samples; i++) { + int err; + + err = encode_value(data_to_encode[i].EXPOSURE_FLAGS, 8, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].FX, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].NCOB_X, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].NCOB_Y, 32, cfg, enc); + if (err) + return err; + } + return 0; +} + + +static int encode_S_FX_EFX_NCOB_ECOB(struct cmp_cfg *cfg, struct encoder_struct + *enc) +{ + struct S_FX_EFX_NCOB_ECOB *data_to_encode = cfg->input_buf; + size_t i; + + for (i = 0; i < cfg->samples; i++) { + int err; + + err = encode_value(data_to_encode[i].EXPOSURE_FLAGS, 8, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].FX, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].NCOB_X, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].NCOB_Y, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].EFX, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].ECOB_X, 32, cfg, enc); + if (err) + return err; + + err = encode_value(data_to_encode[i].ECOB_Y, 32, cfg, enc); + if (err) + return err; + } + return 0; +} + +/* pad the bitstream with zeros */ +int pad_bitstream(struct cmp_cfg *cfg, uint32_t cmp_size, struct cmp_info *info) +{ + int n_bits = 0; + + if (!cfg) + return -1; + + /* is padding needed */ + if (!raw_mode_is_used(cfg->cmp_mode) && cmp_size) { + int n_pad_bits = 32U - (cmp_size & 0x1f); + if (n_pad_bits < 32) { + n_bits = put_n_bits32(0, cmp_size, n_pad_bits, + cfg->icu_output_buf, + cfg->buffer_length); + if (n_bits <= 0) { + /* the icu_output_buf is to small to store the whole bitstream */ + if (info) { + info->cmp_err |= 1UL << SMALL_BUFFER_ERR_BIT; /* set small buffer error */ + info->cmp_size = 0; + } + return -2; + } + } + } + return n_bits; +} + + +static int encode_data(struct cmp_cfg *cfg, struct cmp_info *info) +{ + struct encoder_struct enc; + int err, n_bits; + + enc.encoder = select_encoder(cfg->golomb_par); + enc.log2_golomb_par = ilog_2(cfg->golomb_par); + enc.cmp_size = 0; + + switch (cfg->cmp_mode) { + case MODE_RAW: + err = encode_raw_16(cfg, &enc); + break; + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + err = encode_16((uint16_t *)cfg->input_buf, cfg, &enc); + break; + case MODE_RAW_S_FX: + err = encode_raw_S_FX(cfg, &enc); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + err = encode_S_FX(cfg, &enc); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + err = encode_S_FX_EFX(cfg, &enc); + break; + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + err = encode_S_FX_NCOB(cfg, &enc); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + err = encode_S_FX_EFX_NCOB_ECOB(cfg, &enc); + break; + case MODE_MODEL_ZERO_32: + case MODE_MODEL_MULTI_32: + case MODE_DIFF_ZERO_32: + case MODE_DIFF_MULTI_32: + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + err = encode_32(cfg, &enc); + break; + default: + debug_print("Error: Compression mode not supported.\n"); + return -1; + break; + } + + if (err == -2) { + /* the icu_output_buf is to small to store the whole bitstream */ + info->cmp_err |= 1UL << SMALL_BUFFER_ERR_BIT; /* set small buffer error */ + return err; + } + if (info) + info->cmp_size = enc.cmp_size; + + n_bits = pad_bitstream(cfg, enc.cmp_size, info); + if (n_bits < 0) + return n_bits; + +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + { + size_t i; + uint32_t *p = (uint32_t *)cfg->icu_output_buf; + for (i = 0; i < (enc.cmp_size + n_bits)/32; i++) + cpu_to_be32s(&p[i]); + } +#endif /*__BYTE_ORDER__ */ + + return 0; +} + + +/** + * @brief compress data on the ICU + * + * @param cfg compressor configuration contains all parameters required for + * compression + * @param info compressor information contains information of the executed + * compression + * + * @note this function violates the input_buf in place + * @note if icu_new_model_buf = model_buf or NULL, the model will be updated in place + * @note the validity of the cfg structure is checked before the compression is + * started + * @note when using the 1d-differencing mode or the raw mode (cmp_mode = 0,2,4), + * the model parameters (model_value, model_buf, rdcu_model_adr) are ignored + * @note the rdcu_***_adr configuration parameters are ignored for icu + * compression + * @note semi adaptive compression not supported; configuration parameters + * ap1\_golomb\_par, ap2\_golomb\_par, ap1\_spill ap2\_spill will be + * ignored; information parameters ap1_cmp_size, ap2_cmp_size will always + * be 0 + * + * @returns 0 on success, error otherwise + */ + +int icu_compress_data(struct cmp_cfg *cfg, struct cmp_info *info) +{ + int err; + + err = set_info(cfg, info); + if (err) + return err; + + err = icu_cmp_cfg_valid(cfg, info); + if (err) + return err; + + err = pre_process(cfg); + if (err) + return err; + + err = map_to_pos(cfg); + if (err) + return err; + + err = encode_data(cfg, info); + if (err) + return err; + + return 0; +} diff --git a/lib/cmp_rdcu.c b/lib/cmp_rdcu.c new file mode 100644 index 0000000..caf9c6e --- /dev/null +++ b/lib/cmp_rdcu.c @@ -0,0 +1,876 @@ +/** + * @file cmp_rdcu.c + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at), + * @date 2019 + * + * @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 hardware compressor control library + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + +#include <stdint.h> +#include <stdio.h> + +#include "../include/rdcu_cmd.h" +#include "../include/cmp_support.h" +#include "../include/rdcu_ctrl.h" +#include "../include/rdcu_rmap.h" + + +#define RDCU_INTR_SIG_ENA 1 /* RDCU interrupt signal enabled */ +#define RDCU_INTR_SIG_DIS 0 /* RDCU interrupt signal disable */ +#define RDCU_INTR_SIG_DEFAULT RDCU_INTR_SIG_ENA /* default start value for RDCU + interrupt signal */ +/* RDCU interrupt signal status */ +static int interrupt_signal_enabled = RDCU_INTR_SIG_DEFAULT; + + +/** + * @brief save repeating 3 lines of code... + * + * @note This function depends on the SpW implantation and must be adjusted to it. + * + * @note prints abort message if pending status is non-zero after 10 retries + */ + +static void sync(void) +{ +#if 0 + not needed for packed generation + + int cnt = 0; + printf("syncing..."); + while (rdcu_rmap_sync_status()) { + printf("pending: %d\n", rdcu_rmap_sync_status()); + + if (cnt++ > 10) { + printf("aborting; de"); + break; + } + + } + printf("synced\n"); +#endif +} + + +/** + * @brief check if a buffer is in inside the RDCU SRAM + * + * @param addr start address of the buffer + * @param size length of the buffer in bytes + * + * @returns 1 if buffer in inside the RDCU SRAM, 0 when the buffer is outside + */ + +static int in_sram_range(uint32_t addr, uint32_t size) +{ + if (addr > RDCU_SRAM_END) + return 0; + + if (size > RDCU_SRAM_SIZE) + return 0; + + if (addr + size > RDCU_SRAM_END) + return 0; + + return 1; +} + + +/** + * @brief check if two buffers are overlapping + * @note implement according to https://stackoverflow.com/a/325964 + * + * @param start_a start address of the 1st buffer + * @param end_a end address of the 1st buffer + * @param start_b start address of the 2nd buffer + * @param end_b end address of the 2nd buffer + * + * @returns 0 if buffers are not overlapping, otherwise buffer are + * overlapping + */ +static int buffers_overlap(uint32_t start_a, uint32_t end_a, uint32_t start_b, + uint32_t end_b) +{ + if (start_a < end_b && end_a > start_b) + return 1; + else + return 0; +} + + +/** + * @brief check if the compressor configuration is valid for a RDCU compression, + * see the user manual for more information (PLATO-UVIE-PL-UM-0001). + * + * @param cfg configuration contains all parameters required for compression + * + * @returns >= 0 on success, error otherwise + */ + +int rdcu_cmp_cfg_valid(const struct cmp_cfg *cfg) +{ + int cfg_invalid = 0; + int cfg_warning = 0; + + if (cfg == NULL) + return -1; + + if (cfg->cmp_mode > MAX_RDCU_CMP_MODE) { + printf("Error: selected cmp_mode: %u is not supported. " + "Largest supported mode is: %lu.\n", cfg->cmp_mode, + MAX_RDCU_CMP_MODE); + cfg_invalid++; + } + + if (cfg->model_value > MAX_MODEL_VALUE) { + printf("Error: selected model_value: %u is invalid. " + "Largest supported value is: %lu.\n", cfg->model_value, + MAX_MODEL_VALUE); + cfg_invalid++; + } + + if (cfg->golomb_par < MIN_RDCU_GOLOMB_PAR|| + cfg->golomb_par > MAX_RDCU_GOLOMB_PAR) { + printf("Error: The selected Golomb parameter: %u is not supported. " + "The Golomb parameter has to be between [%lu, %lu].\n", + cfg->golomb_par, MIN_RDCU_GOLOMB_PAR, + MAX_RDCU_GOLOMB_PAR); + cfg_invalid++; + } + + if (cfg->ap1_golomb_par < MIN_RDCU_GOLOMB_PAR || + cfg->ap1_golomb_par > MAX_RDCU_GOLOMB_PAR) { + printf("Error: The selected adaptive 1 Golomb parameter: %u is not supported. " + "The Golomb parameter has to be between [%lu, %lu].\n", + cfg->ap1_golomb_par, MIN_RDCU_GOLOMB_PAR, + MAX_RDCU_GOLOMB_PAR); + cfg_invalid++; + } + + if (cfg->ap2_golomb_par < MIN_RDCU_GOLOMB_PAR || + cfg->ap2_golomb_par > MAX_RDCU_GOLOMB_PAR) { + printf("Error: The selected adaptive 2 Golomb parameter: %u is not supported. " + "The Golomb parameter has to be between [%lu, %lu].\n", + cfg->ap2_golomb_par, MIN_RDCU_GOLOMB_PAR, + MAX_RDCU_GOLOMB_PAR); + cfg_invalid++; + } + + if (cfg->spill < MIN_RDCU_SPILL) { + printf("Error: The selected spillover threshold value: %u is too small. " + "Smallest possible spillover value is: %lu.\n", + cfg->spill, MIN_RDCU_SPILL); + cfg_invalid++; + } + + if (cfg->spill > get_max_spill(cfg->golomb_par, cfg->cmp_mode)) { + printf("Error: The selected spillover threshold value: %u is " + "too large for the selected Golomb parameter: %u, the " + "largest possible spillover value is: %u.\n", + cfg->spill, cfg->golomb_par, + get_max_spill(cfg->golomb_par, cfg->cmp_mode)); + cfg_invalid++; + } + + if (cfg->ap1_spill < MIN_RDCU_SPILL) { + printf("Error: The selected adaptive 1 spillover threshold " + "value: %u is too small. " + "Smallest possible spillover value is: %lu.\n", + cfg->ap1_spill, MIN_RDCU_SPILL); + cfg_invalid++; + } + + if (cfg->ap1_spill > get_max_spill(cfg->ap1_golomb_par, cfg->cmp_mode)) { + printf("Error: The selected adaptive 1 spillover threshold " + "value: %u is too large for the selected adaptive 1 " + "Golomb parameter: %u, the largest possible adaptive 1 " + "spillover value is: %u.\n", + cfg->ap1_spill, cfg->ap1_golomb_par, + get_max_spill(cfg->ap1_golomb_par, cfg->cmp_mode)); + cfg_invalid++; + } + + if (cfg->ap2_spill < MIN_RDCU_SPILL) { + printf("Error: The selected adaptive 2 spillover threshold " + "value: %u is too small." + "Smallest possible spillover value is: %lu.\n", + cfg->ap2_spill, MIN_RDCU_SPILL); + cfg_invalid++; + } + + if (cfg->ap2_spill > get_max_spill(cfg->ap2_golomb_par, cfg->cmp_mode)) { + printf("Error: The selected adaptive 2 spillover threshold " + "value: %u is too large for the selected adaptive 2 " + "Golomb parameter: %u, the largest possible adaptive 2 " + "spillover value is: %u.\n", + cfg->ap2_spill, cfg->ap2_golomb_par, + get_max_spill(cfg->ap2_golomb_par, cfg->cmp_mode)); + cfg_invalid++; + } + + if (cfg->round > MAX_RDCU_ROUND) { + printf("Error: selected round parameter: %u is not supported. " + "Largest supported value is: %lu.\n", + cfg->round, MAX_RDCU_ROUND); + cfg_invalid++; + } + + if (cfg->samples == 0) { + printf("Warning: The samples parameter is set to 0. No data will be compressed.\n"); + cfg_warning++; + } + + if (cfg->buffer_length == 0) { + printf("Error: The buffer_length is set to 0. There is no place " + "to store the compressed data.\n"); + cfg_invalid++; + } + + if (cfg->cmp_mode == MODE_RAW) { + if (cfg->buffer_length < cfg->samples) { + printf("buffer_length is smaller than samples parameter. " + "There is not enough space to copy the data in " + "RAW mode.\n"); + cfg_invalid++; + } + } + + if (!cfg->input_buf) { + printf("Warning: The data to compress buffer is set to NULL. " + "No data will be transferred to the rdcu_data_adr in " + "the RDCU-SRAM.\n"); + cfg_warning++; + } + + if (cfg->rdcu_data_adr & 0x3) { + printf("Error: The RDCU data to compress start address is not 4-Byte aligned.\n"); + cfg_invalid++; + } + + if (cfg->rdcu_buffer_adr & 0x3) { + printf("Error: The RDCU compressed data start address is not 4-Byte aligned.\n"); + cfg_invalid++; + } + + if (!in_sram_range(cfg->rdcu_data_adr, cfg->samples * SAM2BYT)) { + printf("Error: The RDCU data to compress buffer is outside the RDCU SRAM address space.\n"); + cfg_invalid++; + } + + if (!in_sram_range(cfg->rdcu_buffer_adr, cfg->buffer_length * SAM2BYT)) { + printf("Error: The RDCU compressed data buffer is outside the RDCU SRAM address space.\n"); + cfg_invalid++; + } + + if (buffers_overlap(cfg->rdcu_data_adr, + cfg->rdcu_data_adr + cfg->samples * SAM2BYT, + cfg->rdcu_buffer_adr, + cfg->rdcu_buffer_adr + cfg->buffer_length * SAM2BYT)) { + printf("Error: The RDCU data to compress buffer and the RDCU " + "compressed data buffer are overlapping.\n"); + cfg_invalid++; + } + + if (model_mode_is_used(cfg->cmp_mode)) { + if (cfg->model_buf == cfg->input_buf) { + printf("Error: The model buffer (model_buf) and the data " + "to be compressed (input_buf) are equal."); + cfg_invalid++; + } + + if (!cfg->model_buf) { + printf("Warning: The model buffer is set to NULL. No " + "model data will be transferred to the " + "rdcu_model_adr in the RDCU-SRAM.\n"); + cfg_warning++; + } + + if (cfg->rdcu_model_adr & 0x3) { + printf("Error: The RDCU model start address is not 4-Byte aligned.\n"); + cfg_invalid++; + } + + if (!in_sram_range(cfg->rdcu_model_adr, cfg->samples * SAM2BYT)) { + printf("Error: The RDCU model buffer is outside the RDCU SRAM address space.\n"); + cfg_invalid++; + } + + if (buffers_overlap( + cfg->rdcu_model_adr, + cfg->rdcu_model_adr + cfg->samples * SAM2BYT, + cfg->rdcu_data_adr, + cfg->rdcu_data_adr + cfg->samples * SAM2BYT)) { + printf("Error: The model buffer and the data to compress buffer are overlapping.\n"); + cfg_invalid++; + } + + if (buffers_overlap( + cfg->rdcu_model_adr, + cfg->rdcu_model_adr + cfg->samples * SAM2BYT, + cfg->rdcu_buffer_adr, + cfg->rdcu_buffer_adr + cfg->buffer_length * SAM2BYT) + ){ + printf("Error: The model buffer and the compressed data buffer are overlapping.\n"); + cfg_invalid++; + } + + if (cfg->rdcu_model_adr != cfg->rdcu_new_model_adr) { + if (cfg->rdcu_new_model_adr & 0x3) { + printf("Error: The RDCU updated model start address " + "(rdcu_new_model_adr) is not 4-Byte aligned.\n"); + cfg_invalid++; + } + + if (!in_sram_range(cfg->rdcu_new_model_adr, + cfg->samples * SAM2BYT)) { + printf("Error: The RDCU updated model buffer is " + "outside the RDCU SRAM address space.\n"); + cfg_invalid++; + } + + if (buffers_overlap( + cfg->rdcu_new_model_adr, + cfg->rdcu_new_model_adr + cfg->samples * SAM2BYT, + cfg->rdcu_data_adr, + cfg->rdcu_data_adr + cfg->samples * SAM2BYT) + ){ + printf("Error: The updated model buffer and the data to " + "compress buffer are overlapping.\n"); + cfg_invalid++; + } + + if (buffers_overlap( + cfg->rdcu_new_model_adr, + cfg->rdcu_new_model_adr + cfg->samples * SAM2BYT, + cfg->rdcu_buffer_adr, + cfg->rdcu_buffer_adr + cfg->buffer_length * SAM2BYT) + ){ + printf("Error: The updated model buffer and the compressed " + "data buffer are overlapping.\n"); + cfg_invalid++; + } + if (buffers_overlap( + cfg->rdcu_new_model_adr, + cfg->rdcu_new_model_adr + cfg->samples * SAM2BYT, + cfg->rdcu_model_adr, + cfg->rdcu_model_adr + cfg->samples * SAM2BYT) + ){ + printf("Error: The updated model buffer and the " + "model buffer are overlapping.\n"); + cfg_invalid++; + } + } + } + + if (cfg->icu_new_model_buf) { + printf("Warning: ICU updated model buffer is set. This " + "buffer is not used for an RDCU compression.\n"); + cfg_warning++; + } + + if (cfg->icu_output_buf) { + printf("Warning: ICU compressed data buffer is set. This " + "buffer is not used for an RDCU compression.\n"); + cfg_warning++; + } + +#ifdef SKIP_CMP_PAR_CHECK + return 0; +#endif + if (cfg_invalid) + return -cfg_invalid; + else + return cfg_warning; +} + + +/** + * @brief set up RDCU compression register + * + * @param cfg configuration contains all parameters required for compression + * + * @returns 0 on success, error otherwise + */ + +int rdcu_set_compression_register(const struct cmp_cfg *cfg) +{ + if (rdcu_cmp_cfg_valid(cfg) < 0) + return -1; + + /* first, set compression parameters in local mirror registers */ + if (rdcu_set_compression_mode(cfg->cmp_mode)) + return -1; + if (rdcu_set_golomb_param(cfg->golomb_par)) + return -1; + if (rdcu_set_spillover_threshold(cfg->spill)) + return -1; + if (rdcu_set_weighting_param(cfg->model_value)) + return -1; + if (rdcu_set_noise_bits_rounded(cfg->round)) + return -1; + + if (rdcu_set_adaptive_1_golomb_param(cfg->ap1_golomb_par)) + return -1; + if (rdcu_set_adaptive_1_spillover_threshold(cfg->ap1_spill)) + return -1; + + if (rdcu_set_adaptive_2_golomb_param(cfg->ap2_golomb_par)) + return -1; + if (rdcu_set_adaptive_2_spillover_threshold(cfg->ap2_spill)) + return -1; + + if (rdcu_set_data_start_addr(cfg->rdcu_data_adr)) + return -1; + if (rdcu_set_model_start_addr(cfg->rdcu_model_adr)) + return -1; + if (rdcu_set_num_samples(cfg->samples)) + return -1; + if (rdcu_set_new_model_start_addr(cfg->rdcu_new_model_adr)) + return -1; + + if (rdcu_set_compr_data_buf_start_addr(cfg->rdcu_buffer_adr)) + return -1; + if (rdcu_set_compr_data_buf_len(cfg->buffer_length)) + return -1; + + /* now sync the configuration registers to the RDCU... */ + if (rdcu_sync_compressor_param1()) + return -1; + if (rdcu_sync_compressor_param2()) + return -1; + if (rdcu_sync_adaptive_param1()) + return -1; + if (rdcu_sync_adaptive_param2()) + return -1; + if (rdcu_sync_data_start_addr()) + return -1; + if (rdcu_sync_model_start_addr()) + return -1; + if (rdcu_sync_num_samples()) + return -1; + if (rdcu_sync_new_model_start_addr()) + return -1; + if (rdcu_sync_compr_data_buf_start_addr()) + return -1; + if (rdcu_sync_compr_data_buf_len()) + return -1; + + return 0; +} + + +/** + * @brief start the RDCU data compressor + * + * @returns 0 on success, error otherwise + */ + +int rdcu_start_compression(void) +{ + if (interrupt_signal_enabled) { + /* enable the interrupt signal to the ICU */ + rdcu_set_rdcu_interrupt(); + } else { + /* disable the interrupt signal to the ICU */ + rdcu_clear_rdcu_interrupt(); + } + + /* start the compression */ + rdcu_set_data_compr_start(); + if (rdcu_sync_compr_ctrl()) + return -1; + sync(); + + /* clear local bit immediately, this is a write-only register. + * we would not want to restart compression by accidentally calling + * rdcu_sync_compr_ctrl() again + */ + rdcu_clear_data_compr_start(); + + return 0; +} + + +/** + * @brief compressing data with the help of the RDCU hardware compressor + * + * @param cfg configuration contains all parameters required for compression + * + * @note when using the 1d-differencing mode or the raw mode (cmp_mode = 0,2,4), + * the model parameters (model_value, model_buf, rdcu_model_adr) are ignored + * @note the icu_output_buf will not be used for the RDCU compression + * @note the overlapping of the different rdcu buffers is not checked + * @note the validity of the cfg structure is checked before the compression is + * started + * + * @returns 0 on success, error otherwise + */ + +int rdcu_compress_data(const struct cmp_cfg *cfg) +{ + if (!cfg) + return -1; + + if (rdcu_set_compression_register(cfg)) + return -1; + + if (cfg->input_buf != NULL) { + /* round up needed size must be a multiple of 4 bytes */ + uint32_t size = (cfg->samples * 2 + 3) & ~3U; + /* now set the data in the local mirror... */ + if (rdcu_write_sram_16(cfg->input_buf, cfg->rdcu_data_adr, + cfg->samples * 2) < 0) + return -1; + if (rdcu_sync_mirror_to_sram(cfg->rdcu_data_adr, size, + rdcu_get_data_mtu())) + return -1; + } + /*...and the model when needed */ + if (cfg->model_buf != NULL) { + /* set model only when model mode is used */ + if (model_mode_is_used(cfg->cmp_mode)) { + /* round up needed size must be a multiple of 4 bytes */ + uint32_t size = (cfg->samples * 2 + 3) & ~3U; + /* set the model in the local mirror... */ + if (rdcu_write_sram_16(cfg->model_buf, + cfg->rdcu_model_adr, + cfg->samples * 2) < 0) + return -1; + if (rdcu_sync_mirror_to_sram(cfg->rdcu_model_adr, size, + rdcu_get_data_mtu())) + return -1; + } + } + + /* ...and wait for completion */ + sync(); + + /* start the compression */ + if (rdcu_start_compression()) + return -1; + + return 0; +} + + +/** + * @brief read out the status register of the RDCU compressor + * + * @param status compressor status contains the stats of the HW compressor + * + * @note access to the status registers is also possible during compression + * + * @returns 0 on success, error otherwise + */ + +int rdcu_read_cmp_status(struct cmp_status *status) +{ + + if (rdcu_sync_compr_status()) + return -1; + sync(); + + if (status) { + status->data_valid = (uint8_t)rdcu_get_compr_status_valid(); + status->cmp_ready = (uint8_t)rdcu_get_data_compr_ready(); + status->cmp_interrupted = (uint8_t)rdcu_get_data_compr_interrupted(); + status->cmp_active = (uint8_t)rdcu_get_data_compr_active(); + status->rdcu_interrupt_en = (uint8_t)rdcu_get_rdcu_interrupt_enabled(); + } + return 0; +} + + +/** + * @brief read out the metadata of a RDCU compression + * + * @param info compression information contains the metadata of a compression + * + * @note the compression information registers cannot be accessed during a + * compression + * + * @returns 0 on success, error otherwise + */ + +int rdcu_read_cmp_info(struct cmp_info *info) +{ + /* read out the compressor information register*/ + if (rdcu_sync_used_param1()) + return -1; + if (rdcu_sync_used_param2()) + return -1; + if (rdcu_sync_compr_data_start_addr()) + return -1; + if (rdcu_sync_compr_data_size()) + return -1; + if (rdcu_sync_compr_data_adaptive_1_size()) + return -1; + if (rdcu_sync_compr_data_adaptive_2_size()) + return -1; + if (rdcu_sync_compr_error()) + return -1; + if (rdcu_sync_new_model_addr_used()) + return -1; + if (rdcu_sync_samples_used()) + return -1; + + sync(); + + if (info) { + /* put the data in the cmp_info structure */ + info->cmp_mode_used = rdcu_get_compression_mode(); + info->golomb_par_used = rdcu_get_golomb_param(); + info->spill_used = rdcu_get_spillover_threshold(); + info->model_value_used = rdcu_get_weighting_param(); + info->round_used = rdcu_get_noise_bits_rounded(); + info->rdcu_new_model_adr_used = rdcu_get_new_model_addr_used(); + info->samples_used = rdcu_get_samples_used(); + info->rdcu_cmp_adr_used = rdcu_get_compr_data_start_addr(); + info->cmp_size = rdcu_get_compr_data_size(); + info->ap1_cmp_size = rdcu_get_compr_data_adaptive_1_size(); + info->ap2_cmp_size = rdcu_get_compr_data_adaptive_2_size(); + info->cmp_err = rdcu_get_compr_error(); + } + return 0; +} + + +/** + * @brief read the compressed bitstream from the RDCU SRAM + * + * @param info compression information contains the metadata of a compression + * + * @param output_buf the buffer to store the bitstream (if NULL, the required + * size is returned) + * + * @returns the number of bytes read, < 0 on error + */ + +int rdcu_read_cmp_bitstream(const struct cmp_info *info, void *output_buf) +{ + uint32_t s; + + if (info == NULL) + return -1; + + /* calculate the need bytes for the bitstream */ + s = size_of_bitstream(info->cmp_size); + + if (output_buf == NULL) + return (int)s; + + if (rdcu_sync_sram_to_mirror(info->rdcu_cmp_adr_used, s, + rdcu_get_data_mtu())) + return -1; + + /* wait for it */ + sync(); + + return rdcu_read_sram(output_buf, info->rdcu_cmp_adr_used, s); +} + + +/** + * @brief read the model from the RDCU SRAM + * + * @param info compression information contains the metadata of a compression + * + * @param model_buf the buffer to store the model (if NULL, the required size + * is returned) + * + * @returns the number of bytes read, < 0 on error + */ + +int rdcu_read_model(const struct cmp_info *info, void *model_buf) +{ + uint32_t s; + + if (info == NULL) + return -1; + + /* calculate the need bytes for the model */ + s = size_of_model(info->samples_used, info->cmp_mode_used); + + if (model_buf == NULL) + return (int)s; + + if (rdcu_sync_sram_to_mirror(info->rdcu_new_model_adr_used, (s+3) & ~3U, + rdcu_get_data_mtu())) + return -1; + + /* wait for it */ + sync(); + + return rdcu_read_sram(model_buf, info->rdcu_new_model_adr_used, s); +} + + +/** + * @brief interrupt a data compression + * + * @returns 0 on success, error otherwise + */ + +int rdcu_interrupt_compression(void) +{ + /* interrupt a compression */ + rdcu_set_data_compr_interrupt(); + if (rdcu_sync_compr_ctrl()) + return -1; + sync(); + + /* clear local bit immediately, this is a write-only register. + * we would not want to restart compression by accidentially calling + * rdcu_sync_compr_ctrl() again + */ + rdcu_clear_data_compr_interrupt(); + + return 0; +} + + +/** + * @brief enable the RDCU to signal a finished compression with an interrupt signal + */ + +void rdcu_enable_interrput_signal(void) +{ + interrupt_signal_enabled = RDCU_INTR_SIG_ENA; +} + + +/** + * @brief deactivated the RDCU interrupt signal + */ + +void rdcu_disable_interrput_signal(void) +{ + interrupt_signal_enabled = RDCU_INTR_SIG_DIS; +} + + +/** + * @brief compressing data with the help of the RDCU hardware compressor; read + * data from the last compression before starting compression + * + * @param cfg configuration contains all parameters required for compression + * @param last_info compression information of last compression run + * + * @note when using the 1d-differencing mode or the raw mode (cmp_mode = 0,2,4), + * the model parameters (model_value, model_buf, rdcu_model_adr) are ignored + * @note the overlapping of the different rdcu buffers is not checked + * @note the validity of the cfg structure is checked before the compression is + * started + * + * @returns 0 on success, error otherwise + */ + +int rdcu_compress_data_parallel(const struct cmp_cfg *cfg, + const struct cmp_info *last_info) +{ + uint32_t samples_4byte; + + if (!cfg) + return -1; + + if (!last_info) + return rdcu_compress_data(cfg); + + if (last_info->cmp_err) + return -1; + + /* TODO: check read write buffer overlapping */ + + rdcu_set_compression_register(cfg); + + /* round up needed size must be a multiple of 4 bytes */ + samples_4byte = (cfg->samples * SAM2BYT + 3) & ~3U; + + if (cfg->input_buf != NULL) { + uint32_t cmp_size_4byte; + + /* now set the data in the local mirror... */ + if (rdcu_write_sram_16(cfg->input_buf, cfg->rdcu_data_adr, + cfg->samples * SAM2BYT) < 0) + return -1; + + /* calculate the need bytes for the bitstream */ + cmp_size_4byte = ((last_info->cmp_size >> 3) + 3) & ~0x3U; + + /* parallel read compressed data and write input data from sram + * to mirror*/ + if (rdcu_sync_sram_mirror_parallel(last_info->rdcu_cmp_adr_used, + cmp_size_4byte, cfg->rdcu_data_adr, samples_4byte, + rdcu_get_data_mtu())) + return -1; + /* wait for it */ + sync(); + if (cfg->icu_output_buf) { + if (rdcu_read_sram(cfg->icu_output_buf, + last_info->rdcu_cmp_adr_used, + cmp_size_4byte)) + return -1; + } + } else { + printf("Warning: input_buf = NULL; input_buf is not written to the sram and compressed data is not read from the sram\n"); + } + + /* read model and write model in parallel */ + if (cfg->model_buf && model_mode_is_used(cfg->cmp_mode) && model_mode_is_used(last_info->cmp_mode_used)) { + uint32_t new_model_size_4byte; + + /* set the model in the local mirror... */ + if (rdcu_write_sram_16(cfg->model_buf, cfg->rdcu_model_adr, + cfg->samples * SAM2BYT) < 0) + return -1; + + new_model_size_4byte = last_info->samples_used * SAM2BYT ; + if (rdcu_sync_sram_mirror_parallel(last_info->rdcu_new_model_adr_used, + (new_model_size_4byte+3) & ~0x3U, + cfg->rdcu_model_adr, + samples_4byte, rdcu_get_data_mtu())) + return -1; + /* wait for it */ + sync(); + if (cfg->icu_new_model_buf) { + if (rdcu_read_sram(cfg->icu_new_model_buf, + last_info->rdcu_new_model_adr_used, + new_model_size_4byte)) + return -1; + } + /* write model */ + } else if (cfg->model_buf && model_mode_is_used(cfg->cmp_mode)) { + /* set the model in the local mirror... */ + if (rdcu_write_sram_16(cfg->model_buf, cfg->rdcu_model_adr, + cfg->samples * SAM2BYT) < 0) + return -1; + + if (rdcu_sync_mirror_to_sram(cfg->rdcu_model_adr, samples_4byte, + rdcu_get_data_mtu())) + return -1; + /* read model */ + } else if (model_mode_is_used(last_info->cmp_mode_used)) { + if (rdcu_read_model(last_info, cfg->icu_new_model_buf) < 0) + return -1; + } + + /* ...and wait for completion */ + sync(); + + if (rdcu_start_compression()) + return -1; + + return 0; +} + diff --git a/lib/cmp_support.c b/lib/cmp_support.c new file mode 100644 index 0000000..39aa61f --- /dev/null +++ b/lib/cmp_support.c @@ -0,0 +1,595 @@ +/** + * @file cmp_support.c + * @author Dominik Loidolt (dominik.loidolt@univie.ac.at), + * @date 2019 + * + * @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 compressor support library + * @see Data Compression User Manual PLATO-UVIE-PL-UM-0001 + */ + +#include <stdio.h> + +#include "../include/cmp_support.h" +#include "../include/cmp_data_types.h" +#include "../include/cmp_debug.h" + + +/** + * @brief Default configuration of the Compressor in model imagette mode. + * @warning All ICU buffers are set to NULL. Samples and buffer_length are set to 0 + * @note see PLATO-IWF-PL-RS-0005 V1.1 + */ + + +const struct cmp_cfg DEFAULT_CFG_MODEL = { + MODE_MODEL_MULTI, /* cmp_mode */ + 4, /* golomb_par */ + 48, /* spill */ + 8, /* model_value */ + 0, /* round */ + 3, /* ap1_golomb_par */ + 35, /* ap1_spill */ + 5, /* ap2_golomb_par */ + 60, /* ap2_spill */ + NULL, /* *input_buf */ + 0x000000, /* rdcu_data_adr */ + NULL, /* *model_buf */ + 0x200000, /* rdcu_model_adr */ + NULL, /* *icu_new_model_buf */ + 0x400000, /* rdcu_up_model_adr */ + 0, /* samples */ + NULL, /* *icu_output_buf */ + 0x600000, /* rdcu_buffer_adr */ + 0x0 /* buffer_length */ +}; + + +/** + * @brief Default configuration of the Compressor in 1d-differencing imagette mode. + * @warning All ICU buffers are set to NULL. Samples and buffer_length are set to 0 + * @note see PLATO-IWF-PL-RS-0005 V1.1 + */ + +const struct cmp_cfg DEFAULT_CFG_DIFF = { + MODE_DIFF_ZERO, /* cmp_mode */ + 7, /* golomb_par */ + 60, /* spill */ + 8, /* model_value */ + 0, /* round */ + 6, /* ap1_golomb_par */ + 48, /* ap1_spill */ + 8, /* ap2_golomb_par */ + 72, /* ap2_spill */ + NULL, /* *input_buf */ + 0x000000, /* rdcu_data_adr */ + NULL, /* *model_buf */ + 0x000000, /* rdcu_model_adr */ + NULL, /* *icu_new_model_buf */ + 0x000000, /* rdcu_up_model_adr */ + 0, /* samples */ + NULL, /* *icu_output_buf */ + 0x600000, /* rdcu_buffer_adr */ + 0x0 /* buffer_length */ +}; + + +/** + * @brief implementation of the logarithm base of floor(log2(x)) for integers + * @note ilog_2(0) = -1 defined + * + * @param x input parameter + * + * @returns the result of floor(log2(x)) + */ + +int ilog_2(uint32_t x) +{ + if (!x) + return -1; + + return 31 - __builtin_clz(x); +} + + +/** + * @brief Determining if an integer is a power of 2 + * @note 0 is incorrectly considered a power of 2 here + * + * @param v we want to see if v is a power of 2 + * + * @returns 1 if v is a power of 2, otherwise 0 + * + * @note see: https://graphics.stanford.edu/~seander/bithacks.html#DetermineIfPowerOf2 + */ + +int is_a_pow_of_2(unsigned int v) +{ + return (((v) & ((v) - 1)) == 0); +} + + +/** + * @brief check if a model mode is selected + * + * @param cmp_mode compression mode + * + * @returns 1 when model mode is set, otherwise 0 + */ + +int model_mode_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_MODEL_ZERO: + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_ZERO_F_FX_EFX: + case MODE_MODEL_ZERO_F_FX_NCOB: + case MODE_MODEL_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI: + case MODE_MODEL_MULTI_S_FX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_F_FX: + case MODE_MODEL_MULTI_F_FX_EFX: + case MODE_MODEL_MULTI_F_FX_NCOB: + case MODE_MODEL_MULTI_F_FX_EFX_NCOB_ECOB: + case MODE_MODEL_ZERO_32: + case MODE_MODEL_MULTI_32: + return 1; + break; + default: + return 0; + break; + } +} + + +/** + * @brief check if a 1d-differencing mode is selected + * + * @param cmp_mode compression mode + * + * @returns 1 when 1d-differencing mode is set, otherwise 0 + */ + +int diff_mode_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_DIFF_ZERO: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_ZERO_F_FX_EFX: + case MODE_DIFF_ZERO_F_FX_NCOB: + case MODE_DIFF_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI: + case MODE_DIFF_MULTI_S_FX: + case MODE_DIFF_MULTI_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_F_FX: + case MODE_DIFF_MULTI_F_FX_EFX: + case MODE_DIFF_MULTI_F_FX_NCOB: + case MODE_DIFF_MULTI_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_32: + case MODE_DIFF_MULTI_32: + return 1; + break; + default: + return 0; + break; + } +} + + +/** + * @brief check if the raw mode is selected + * + * @param cmp_mode compression mode + * + * @returns 1 when raw mode is set, otherwise 0 + */ + +int raw_mode_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_RAW: + case MODE_RAW_S_FX: + case MODE_RAW_32: + return 1; + break; + default: + return 0; + break; + } +} + + +/** + * @brief check if the mode is supported by the RDCU compressor + * + * @param cmp_mode compression mode + * + * @returns 1 when mode is supported by the RDCU, otherwise 0 + */ + +int rdcu_supported_mode_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_RAW: + case MODE_MODEL_ZERO: + case MODE_DIFF_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_MULTI: + return 1; + break; + default: + return 0; + break; + } +} + + +/** + * @brief check if zero escape symbol mechanism mode is used + * + * @param cmp_mode compression mode + * + * @returns 1 when zero escape symbol mechanism is set, otherwise 0 + */ + +int zero_escape_mech_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_MODEL_ZERO: + case MODE_DIFF_ZERO: + case MODE_MODEL_ZERO_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_ZERO_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_MODEL_ZERO_F_FX_EFX: + case MODE_DIFF_ZERO_F_FX_EFX: + case MODE_MODEL_ZERO_F_FX_NCOB: + case MODE_DIFF_ZERO_F_FX_NCOB: + case MODE_MODEL_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_MODEL_ZERO_32: + case MODE_DIFF_ZERO_32: + return 1; + break; + default: + return 0; + break; + } + +} + + +/** + * @brief check if multi escape symbol mechanism mode is used + * + * @param cmp_mode compression mode + * + * @returns 1 when multi escape symbol mechanism is set, otherwise 0 + */ + +int multi_escape_mech_is_used(unsigned int cmp_mode) +{ + switch (cmp_mode) { + case MODE_MODEL_MULTI: + case MODE_DIFF_MULTI: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_MULTI_S_FX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_MULTI_F_FX: + case MODE_MODEL_MULTI_F_FX_EFX: + case MODE_DIFF_MULTI_F_FX_EFX: + case MODE_MODEL_MULTI_F_FX_NCOB: + case MODE_DIFF_MULTI_F_FX_NCOB: + case MODE_MODEL_MULTI_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_F_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_32: + case MODE_DIFF_MULTI_32: + return 1; + break; + default: + return 0; + break; + } +} + + +/** + * @brief method for lossy rounding + * + * @param value value to round + * @param round round parameter + * + * @return rounded value + */ + +unsigned int round_fwd(unsigned int value, unsigned int round) +{ + return value >> round; +} + + +/** + * @brief inverse method for lossy rounding + * + * @param value value to round back + * @param round round parameter + * + * @return back rounded value + */ + +unsigned int round_inv(unsigned int value, unsigned int round) +{ + return value << round; +} + + +/** + * @brief implantation of the model update equation + * + * @note check before that model_value is not greater than MAX_MODEL_VALUE + + * @param data data to process + * @param model (current) model of the data to process + * @param model_value model weighting parameter + * + * @returns (new) updated model + */ + +unsigned int cal_up_model(unsigned int data, unsigned int model, unsigned int + model_value) +{ + /* cast uint64_t to prevent overflow in the multiplication */ + uint64_t weighted_model = (uint64_t)model * model_value; + uint64_t weighted_data = (uint64_t)data * (MAX_MODEL_VALUE - model_value); + /* truncation is intended */ + return (unsigned int)((weighted_model + weighted_data) / MAX_MODEL_VALUE); +} + + +/** + * @brief get the maximum valid spill threshold value for a given golomb_par + * + * @param golomb_par Golomb parameter + * @param cmp_mode compression mode + * + * @returns the highest still valid spill threshold value + */ + +uint32_t get_max_spill(unsigned int golomb_par, unsigned int cmp_mode) +{ + unsigned int cutoff; + unsigned int max_n_sym_offset; + unsigned int max_cw_bits; + + if (golomb_par == 0) + return 0; + + if (rdcu_supported_mode_is_used(cmp_mode)) { + max_cw_bits = 16; /* the RDCU compressor can only generate code + * words with a length of maximal 16 bits. + */ + } else { + max_cw_bits = 32; /* the ICU compressor can generate code + * words with a length of maximal 32 bits. + */ + } + + cutoff = (1UL << (ilog_2(golomb_par)+1)) - golomb_par; + max_n_sym_offset = max_cw_bits/2 - 1; + + return (max_cw_bits-1-ilog_2(golomb_par))*golomb_par + cutoff - + max_n_sym_offset - 1; +} + + +/** + * @brief calculate the size of a sample for the different compression modes + * + * @param cmp_mode compression mode + * + * @returns the size of a data sample in bytes for the selected compression + * mode; + */ + +size_t size_of_a_sample(unsigned int cmp_mode) +{ + size_t sample_len; + + switch (cmp_mode) { + case MODE_RAW: + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + sample_len = sizeof(uint16_t); + break; + case MODE_RAW_S_FX: + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + sample_len = sizeof(struct S_FX); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + sample_len = sizeof(struct S_FX_NCOB); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + sample_len = sizeof(struct S_FX_EFX_NCOB_ECOB); + break; + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + sample_len = sizeof(struct F_FX); + break; + case MODE_MODEL_ZERO_F_FX_EFX: + case MODE_MODEL_MULTI_F_FX_EFX: + case MODE_DIFF_ZERO_F_FX_EFX: + case MODE_DIFF_MULTI_F_FX_EFX: + sample_len = sizeof(struct F_FX_EFX); + break; + case MODE_MODEL_ZERO_F_FX_NCOB: + case MODE_MODEL_MULTI_F_FX_NCOB: + case MODE_DIFF_ZERO_F_FX_NCOB: + case MODE_DIFF_MULTI_F_FX_NCOB: + sample_len = sizeof(struct F_FX_NCOB); + break; + case MODE_MODEL_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_F_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_F_FX_EFX_NCOB_ECOB: + sample_len = sizeof(struct F_FX_EFX_NCOB_ECOB); + break; + case MODE_RAW_32: + case MODE_MODEL_ZERO_32: + case MODE_MODEL_MULTI_32: + case MODE_DIFF_ZERO_32: + case MODE_DIFF_MULTI_32: + sample_len = sizeof(uint32_t); + break; + default: + debug_print("Error: Compression mode not supported.\n"); + return 0; + break; + } + return sample_len; +} + + +/** + * @brief calculate the need bytes to hold a bitstream + * + * @param cmp_size compressed data size, measured in bits + * + * @returns the size in bytes to sore the hole bitstream + */ + +unsigned int size_of_bitstream(unsigned int cmp_size) +{ + return (((cmp_size >> 3) + 3) & ~0x3U); +} + + +/** + * @brief calculate the need bytes for the model + * + * @param cmp_size compressed data size, measured in bits + * + * @returns the size in bytes to sore the hole bitstream + */ + +unsigned int size_of_model(unsigned int samples, unsigned int cmp_mode) +{ + return samples * size_of_a_sample(cmp_mode); +} + + +/** + * @brief print the cmp_cfg structure + * + * @param cfg compressor configuration contains all parameters required for + * compression + * + */ + +void print_cmp_cfg(const struct cmp_cfg *cfg) +{ + size_t i; + + printf("cmp_mode: %u\n", cfg->cmp_mode); + printf("golomb_par: %u\n", cfg->golomb_par); + printf("spill: %u\n", cfg->spill); + printf("model_value: %u\n", cfg->model_value); + printf("round: %u\n", cfg->round); + printf("ap1_golomb_par: %u\n", cfg->ap1_golomb_par); + printf("ap1_spill: %u\n", cfg->ap1_spill); + printf("ap2_golomb_par: %u\n", cfg->ap2_golomb_par); + printf("ap2_spill: %u\n", cfg->ap2_spill); + printf("input_buf: %p\n", (void *)cfg->input_buf); + if (cfg->input_buf != NULL) { + printf("input data:"); + for (i = 0; i < cfg->samples; i++) { + printf(" %04X", ((uint16_t *)cfg->input_buf)[i]); + } + printf("\n"); + } + printf("rdcu_data_adr: 0x%06X\n", cfg->rdcu_data_adr); + printf("model_buf: %p\n", (void *)cfg->model_buf); + if (cfg->model_buf != NULL) { + printf("model data:"); + for (i = 0; i < cfg->samples; i++) { + printf(" %04X", ((uint16_t *)cfg->model_buf)[i]); + } + printf("\n"); + } + printf("rdcu_model_adr: 0x%06X\n", cfg->rdcu_model_adr); + printf("rdcu_new_model_adr: 0x%06X\n", cfg->rdcu_new_model_adr); + printf("samples: %u\n", cfg->samples); + printf("icu_output_buf: %p\n", (void *)cfg->icu_output_buf); + printf("rdcu_buffer_adr: 0x%06X\n", cfg->rdcu_buffer_adr); + printf("buffer_length: %u\n", cfg->buffer_length); +} + + +/** + * @brief print the cmp_info structure + * + * @param info compressor information contains information of an executed + * compression + */ + +void print_cmp_info(const struct cmp_info *info) +{ + printf("cmp_mode_used: %u\n", info->cmp_mode_used); + printf("model_value_used: %u\n", info->model_value_used); + printf("round_used: %u\n", info->round_used); + printf("spill_used: %u\n", info->spill_used); + printf("golomb_par_used: %u\n", info->golomb_par_used); + printf("samples_used: %u\n", info->samples_used); + printf("cmp_size: %u\n", info->cmp_size); + printf("ap1_cmp_size: %u\n", info->ap1_cmp_size); + printf("ap2_cmp_size: %u\n", info->ap2_cmp_size); + printf("rdcu_new_model_adr_used: 0x%06X\n", info->rdcu_new_model_adr_used); + printf("rdcu_cmp_adr_used: 0x%06X\n", info->rdcu_cmp_adr_used); + printf("cmp_err: %#X\n", info->cmp_err); +} diff --git a/lib/decmp.c b/lib/decmp.c new file mode 100644 index 0000000..1138772 --- /dev/null +++ b/lib/decmp.c @@ -0,0 +1,1534 @@ +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <limits.h> +#include <string.h> + +#include "../include/cmp_support.h" +#include "../include/cmp_icu.h" +#include "../include/cmp_data_types.h" +#include "../include/byteorder.h" +#include "../include/cmp_debug.h" + + +double get_compression_ratio(const struct cmp_info *info) +{ + unsigned long orign_len_bits = info->samples_used * size_of_a_sample(info->cmp_mode_used) * CHAR_BIT; + + return (double)orign_len_bits/(double)info->cmp_size; +} + + +void *malloc_decompressed_data(const struct cmp_info *info) +{ + size_t sample_len; + + if (!info) + return NULL; + + if (info->samples_used == 0) + return NULL; + + sample_len = size_of_a_sample(info->cmp_mode_used); + + return malloc(info->samples_used * sample_len); +} + + +/** + * @brief decompression data pre-processing in RAW mode + * + * @note in RAW mode the data are uncompressed no pre_processing needed + * + * @param cmp_mode_used used compression mode + * + * @returns 0 on success, error otherwise + */ + +static int de_raw_pre_process(uint8_t cmp_mode_used) +{ + if (!raw_mode_is_used(cmp_mode_used)) + return -1; + + return 0; +} + + +/** + * @brief model decompression pre-processing + * + * @note + * + * @param data_buf pointer to the data to process + * @param model_buf pointer to the model of the data to process + * @param samples_used the size of the data and model buffer in 16 bit units + * @param model_value_used used model weighting parameter + * @param round_used used number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int de_model_16(uint16_t *data_buf, uint16_t *model_buf, uint32_t + samples_used, uint8_t model_value_used, uint8_t + round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (!model_buf) + return -1; + + if (model_value_used > MAX_MODEL_VALUE) + return -1; + + for (i = 0; i < samples_used; i++) { + /* overflow is intended */ + data_buf[i] = (uint16_t)(data_buf[i] + round_fwd(model_buf[i], + round_used)); + } + + err = de_lossy_rounding_16(data_buf, samples_used, round_used); + if (err) + return -1; + + for (i = 0; i < samples_used; i++) { + model_buf[i] = (uint16_t)cal_up_model(data_buf[i], model_buf[i], + model_value_used); + } + return 0; +} + + +static int de_model_S_FX(struct S_FX *data_buf, struct S_FX *model_buf, uint32_t + samples_used, uint8_t model_value_used, uint8_t + round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + if (!model_buf) + return -1; + + if (model_value_used > MAX_MODEL_VALUE) + return -1; + + for (i = 0; i < samples_used; i++) { + /* overflow is intended */ + struct S_FX round_model = model_buf[i]; + + lossy_rounding_S_FX(&round_model, 1, round_used); + data_buf[i] = add_S_FX(data_buf[i], model_buf[i]); + } + + err = de_lossy_rounding_S_FX(data_buf, samples_used, round_used); + if (err) + return -1; + + for (i = 0; i < samples_used; i++) + model_buf[i] = cal_up_model_S_FX(data_buf[i], model_buf[i], + model_value_used); + + return 0; +} + + +/** + * @brief 1d-differencing decompression per-processing + * + * @param data_buf pointer to the data to process + * @param samples_used the size of the data and model buffer in 16 bit units + * @param round_used used number of bits to round; if zero no rounding takes place + * + * @returns 0 on success, error otherwise + */ + +static int de_diff_16(uint16_t *data_buf, uint32_t samples_used, uint8_t + round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = data_buf[i] + data_buf[i-1]; + } + + err = de_lossy_rounding_16(data_buf, samples_used, round_used); + if (err) + return -1; + + return 0; +} + + +static int de_diff_32(uint32_t *data_buf, uint32_t samples_used, uint8_t + round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = data_buf[i] + data_buf[i-1]; + } + + err = de_lossy_rounding_32(data_buf, samples_used, round_used); + if (err) + return -1; + + return 0; +} + + +static int de_diff_S_FX(struct S_FX *data_buf, uint32_t samples_used, uint8_t + round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = add_S_FX(data_buf[i], data_buf[i-1]); + } + + err = de_lossy_rounding_S_FX(data_buf, samples_used, round_used); + if (err) + return -1; + + return 0; +} + + +static int de_diff_S_FX_EFX(struct S_FX_EFX *data_buf, uint32_t samples_used, + uint8_t round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = add_S_FX_EFX(data_buf[i], data_buf[i-1]); + } + + err = de_lossy_rounding_S_FX_EFX(data_buf, samples_used, round_used); + if (err) + return -1; + + return 0; +} + + +static int de_diff_S_FX_NCOB(struct S_FX_NCOB *data_buf, uint32_t samples_used, + uint8_t round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = add_S_FX_NCOB(data_buf[i], data_buf[i-1]); + } + + err = de_lossy_rounding_S_FX_NCOB(data_buf, samples_used, round_used); + if (err) + return -1; + + return 0; +} + + +static int de_diff_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + uint32_t samples_used, uint8_t round_used) +{ + size_t i; + int err; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 1; i < samples_used; i++) { + /* overflow intended */ + data_buf[i] = add_S_FX_EFX_NCOB_ECOB(data_buf[i], data_buf[i-1]); + } + + err = de_lossy_rounding_S_FX_EFX_NCOB_ECOB(data_buf, samples_used, + round_used); + if (err) + return -1; + + return 0; +} + + +static int de_pre_process(void *decoded_data, void *de_model_buf, + const struct cmp_info *info) +{ + if (!decoded_data) + return -1; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + switch (info->cmp_mode_used) { + case MODE_RAW: + case MODE_RAW_S_FX: + return de_raw_pre_process(info->cmp_mode_used); + break; + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + return de_model_16((uint16_t *)decoded_data, + (uint16_t *)de_model_buf, info->samples_used, + info->model_value_used, info->round_used); + break; + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + return de_diff_16((uint16_t *)decoded_data, info->samples_used, + info->round_used); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + return de_model_S_FX((struct S_FX *)decoded_data, + (struct S_FX *)de_model_buf, + info->samples_used, info->model_value_used, + info->round_used); + break; + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + return de_diff_S_FX((struct S_FX *)decoded_data, + info->samples_used, info->round_used); + break; + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + return de_diff_S_FX_EFX((struct S_FX_EFX *)decoded_data, + info->samples_used, info->round_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + return -1; + break; + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + return de_diff_S_FX_NCOB((struct S_FX_NCOB *)decoded_data, + info->samples_used, info->round_used); + break; + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_NCOB: + return -1; + break; + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + return de_diff_S_FX_EFX_NCOB_ECOB((struct S_FX_EFX_NCOB_ECOB *) + decoded_data, + info->samples_used, + info->round_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + return -1; + break; + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + return de_diff_32((uint32_t *)decoded_data, info->samples_used, + info->round_used); + break; + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + return -1; + break; + default: + debug_print("Error: Compression mode not supported.\n"); + break; + } + + return -1; +} + + +static uint8_t de_map_to_pos_alg_8(uint8_t value_to_unmap) +{ + if (value_to_unmap & 0x1) /* if uneven */ + return (value_to_unmap + 1) / -2; + else + return value_to_unmap / 2; +} + + +static uint16_t de_map_to_pos_alg_16(uint16_t value_to_unmap) +{ + if (value_to_unmap & 0x1) /* if uneven */ + return (value_to_unmap + 1) / -2; + else + return value_to_unmap / 2; +} + + +static uint32_t de_map_to_pos_alg_32(uint32_t value_to_unmap) +{ + + if (value_to_unmap & 0x1) /* if uneven */ + return ((int64_t)value_to_unmap + 1) / -2; /* typecast to prevent overflow */ + else + return value_to_unmap / 2; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * 16-bit buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the uint16_t data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_16(uint16_t *data_buf, uint32_t samples_used, int + zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) + data_buf[i] -= 1; + + data_buf[i] = (uint16_t)de_map_to_pos_alg_16(data_buf[i]); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * 32-bit buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the uint16_t data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_32(uint32_t *data_buf, uint32_t samples_used, int + zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) + data_buf[i] -= 1; + + data_buf[i] = (uint32_t)de_map_to_pos_alg_32(data_buf[i]); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * S_FX buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the S_FX data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_S_FX(struct S_FX *data_buf, uint32_t samples_used, int + zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS -= 1; */ + data_buf[i].FX -= 1; + } + + data_buf[i].EXPOSURE_FLAGS = + de_map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = de_map_to_pos_alg_32(data_buf[i].FX); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * S_FX_EFX buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the S_FX_EFX data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_S_FX_EFX(struct S_FX_EFX *data_buf, uint32_t + samples_used, int zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS -= 1; */ + data_buf[i].FX -= 1; + data_buf[i].EFX -= 1; + } + + data_buf[i].EXPOSURE_FLAGS = + de_map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = de_map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].EFX = de_map_to_pos_alg_32(data_buf[i].EFX); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * S_FX_NCOB buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the S_FX_NCOB data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_S_FX_NCOB(struct S_FX_NCOB *data_buf, uint32_t + samples_used, int zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS -= 1; */ + data_buf[i].FX -= 1; + data_buf[i].NCOB_X -= 1; + data_buf[i].NCOB_Y -= 1; + } + + data_buf[i].EXPOSURE_FLAGS = + de_map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = de_map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].NCOB_X = de_map_to_pos_alg_32(data_buf[i].NCOB_X); + data_buf[i].NCOB_Y = de_map_to_pos_alg_32(data_buf[i].NCOB_Y); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * S_FX_EFX_NCOB_ECOB buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the S_FX_EFX_NCOB_ECOB data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_S_FX_EFX_NCOB_ECOB(struct S_FX_EFX_NCOB_ECOB *data_buf, + uint32_t samples_used, + int zero_mode_used) +{ + size_t i; + + if (!samples_used) + return 0; + + if (!data_buf) + return -1; + + for (i = 0; i < samples_used; i++) { + if (zero_mode_used) { + /* data_buf[i].EXPOSURE_FLAGS -= 1; */ + data_buf[i].FX -= 1; + data_buf[i].NCOB_X -= 1; + data_buf[i].NCOB_Y -= 1; + data_buf[i].EFX -= 1; + data_buf[i].ECOB_X -= 1; + data_buf[i].ECOB_Y -= 1; + } + + data_buf[i].EXPOSURE_FLAGS = + de_map_to_pos_alg_8(data_buf[i].EXPOSURE_FLAGS); + data_buf[i].FX = de_map_to_pos_alg_32(data_buf[i].FX); + data_buf[i].NCOB_X = de_map_to_pos_alg_32(data_buf[i].NCOB_X); + data_buf[i].NCOB_Y = de_map_to_pos_alg_32(data_buf[i].NCOB_Y); + data_buf[i].EFX = de_map_to_pos_alg_32(data_buf[i].EFX); + data_buf[i].ECOB_X = de_map_to_pos_alg_32(data_buf[i].ECOB_X); + data_buf[i].ECOB_Y = de_map_to_pos_alg_32(data_buf[i].ECOB_Y); + } + return 0; +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range for a + * F_FX buffer + * + * @note change the data_buf in-place + * + * @param data_buf pointer to the F_FX data buffer to process + * @param samples_used amount of data samples in the data_buf + * @param zero_mode_used needs to be set if the zero escape symbol mechanism is used + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos_F_FX(uint32_t *data_buf, uint32_t samples_used, int + zero_mode_used) +{ + return de_map_to_pos_32(data_buf, samples_used, zero_mode_used); +} + + +/** + * @brief map the unsigned output of the pre-stage to a signed value range + * + * @note change the data_buf in-place + * + * @param decompressed_data pointer to the data to process + * @param info compressor information contains information of + * an executed compression + * + * @returns 0 on success, error otherwise + */ + +static int de_map_to_pos(void *decompressed_data, const struct cmp_info *info) +{ + int zero_mode_used; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decompressed_data) + return -1; + + zero_mode_used = zero_escape_mech_is_used(info->cmp_mode_used); + + switch (info->cmp_mode_used) { + case MODE_RAW: + case MODE_RAW_S_FX: + return 0; /* in raw mode no mapping is necessary */ + break; + case MODE_MODEL_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_ZERO: + case MODE_DIFF_MULTI: + return de_map_to_pos_16((uint16_t *)decompressed_data, + info->samples_used, zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + return de_map_to_pos_S_FX((struct S_FX *)decompressed_data, + info->samples_used, zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + return de_map_to_pos_S_FX_EFX((struct S_FX_EFX *) + decompressed_data, + info->samples_used, + zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + return de_map_to_pos_S_FX_NCOB((struct S_FX_NCOB *) + decompressed_data, + info->samples_used, + zero_mode_used); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + return de_map_to_pos_S_FX_EFX_NCOB_ECOB((struct S_FX_EFX_NCOB_ECOB *) + decompressed_data, + info->samples_used, + zero_mode_used); + break; + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + return de_map_to_pos_F_FX((uint32_t *)decompressed_data, + info->samples_used, zero_mode_used); + break; + default: + debug_print("Error: Compression mode not supported.\n"); + break; + } + return -1; +} + + +static unsigned int GetNBits32 (uint32_t *p_value, unsigned int bitOffset, + unsigned int nBits, const unsigned int *srcAddr) +{ + const unsigned int *localAddr; + unsigned int bitsLeft, bitsRight, localEndPos; + unsigned int mask; + /*leave in case of erroneous input */ + if (nBits == 0) + return 0; + if (nBits > 32) + return 0; + if (!srcAddr) + return 0; + if (!p_value) + return 0; + /* separate the bitOffset into word offset (set localAddr pointer) and + * local bit offset (bitsLeft) + */ + localAddr = srcAddr + (bitOffset >> 5); + bitsLeft = bitOffset & 0x1f; + + localEndPos = bitsLeft + nBits; + + if (localEndPos <= 32) { + unsigned int shiftRight = 32 - nBits; + + bitsRight = shiftRight - bitsLeft; + + *(p_value) = *(localAddr) >> bitsRight; + + mask = (0xffffffff >> shiftRight); + + *(p_value) &= mask; + } else { + unsigned int n1 = 32 - bitsLeft; + unsigned int n2 = nBits - n1; + /* part 1 ; */ + mask = 0xffffffff >> bitsLeft; + *(p_value) = (*localAddr) & mask; + *(p_value) <<= n2; + /*part 2: */ + /* adjust address*/ + localAddr += 1; + + bitsRight = 32 - n2; + *(p_value) |= *(localAddr) >> bitsRight; + } + return nBits; +} + +static unsigned int count_leading_ones(unsigned int value) +{ + unsigned int n_ones = 0; /* number of leading 1s */ + + while (1) { + unsigned int leading_bit; + + leading_bit = value & 0x80000000; + if (!leading_bit) + break; + else { + n_ones++; + value <<= 1; + } + } + return n_ones; +} + + +static unsigned int Rice_decoder(uint32_t code_word, unsigned int m, + unsigned int log2_m, unsigned int *decoded_cw) +{ + unsigned int q; /* quotient code */ + unsigned int ql; /* length of the quotient code */ + unsigned int r; /* remainder code */ + unsigned int rl; /* length of the remainder code */ + unsigned int cw_len; /* length of the decoded code word in bits */ + + (void)m; + + q = count_leading_ones(code_word); + ql = q + 1; /* Number of 1's + following 0 */ + + rl = log2_m; + + cw_len = rl + ql; + + if (cw_len > 32) /* can only decode code words with maximum 32 bits */ + return 0; + + code_word = code_word << ql; /* shift quotient code out */ + + /* Right shifting an integer by a number of bits equal orgreater than + * its size is undefined behavior + */ + if (rl == 0) + r = 0; + else + r = code_word >> (32 - rl); + + *decoded_cw = (q << log2_m) + r; + + return cw_len; +} + + +static unsigned int Golomb_decoder(unsigned int code_word, unsigned int m, + unsigned int log2_m, unsigned int + *decoded_cw) +{ + unsigned int q; /* quotient code */ + unsigned int r1; /* remainder code group 1 */ + unsigned int r2; /* remainder code group 2 */ + unsigned int r; /* remainder code */ + unsigned int rl; /* length of the remainder code */ + unsigned int cutoff; /* cutoff between group 1 and 2 */ + unsigned int cw_len; /* length of the decoded code word in bits */ + + q = count_leading_ones(code_word); + + rl = log2_m + 1; + code_word <<= (q+1); /* shift quotient code out */ + + r2 = code_word >> (32 - rl); + r1 = r2 >> 1; + + cutoff = (1UL << rl) - m; + + if (r1 < cutoff) { + cw_len = q + rl; + r = r1; + } else { + cw_len = q + rl + 1; + r = r2 - cutoff; + } + + if (cw_len > 32) + return 0; + + *decoded_cw = q*m + r; + return cw_len; +} + + +typedef unsigned int (*decoder_ptr)(unsigned int, unsigned int, unsigned int, unsigned int *); + +static decoder_ptr select_decoder(unsigned int golomb_par) +{ + if (!golomb_par) + return NULL; + + if (is_a_pow_of_2(golomb_par)) + return &Rice_decoder; + else + return &Golomb_decoder; +} + + +static int decode_raw(const void *compressed_data, const struct cmp_info + *info, void *const decompressed_data) +{ + if (!info) + return -1; + if (info->samples_used == 0) + return 0; + if (!compressed_data) + return -1; + if (!decompressed_data) + return -1; + + if (info->samples_used*size_of_a_sample(info->cmp_mode_used)*CHAR_BIT + != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression.\n"); + return 1; + } + memcpy(decompressed_data, compressed_data, info->cmp_size/CHAR_BIT); + + return 0; +} + +static int decode_raw_16(const void *compressed_data, const struct cmp_info + *info, uint16_t *const decompressed_data) +{ + int err = decode_raw(compressed_data, info, decompressed_data); + if (err) + return err; + +#if defined(__LITTLE_ENDIAN) + { + size_t i; + for (i = 0; i < info->samples_used; i++) { + decompressed_data[i] = cpu_to_be16(decompressed_data[i]); + } + } +#endif + return 0; +} + + +static int decode_raw_S_FX(const void *compressed_data, const struct cmp_info + *info, struct S_FX *const decompressed_data) +{ + int err = decode_raw(compressed_data, info, decompressed_data); + if (err) + return err; + +#if defined(LITTLE_ENDIAN) + { + size_t i; + for (i = 0; i < info->samples_used; i++) { + decompressed_data[i].FX = cpu_to_be32(decompressed_data[i].FX); + } + } +#endif + return 0; +} + +static unsigned int decode_normal(const void *compressed_data, + const struct cmp_info *info, + unsigned int read_pos, + unsigned int max_cw_len, + uint32_t *const decoded_val) +{ + decoder_ptr decoder; + unsigned int n_read_bits; + uint32_t read_val; + unsigned int n_bits; + unsigned int read_bits; + unsigned int log2_g; + + if (!compressed_data) + return -1U; + + if (!info) + return -1U; + + if (!decoded_val) + return -1U; + + if (read_pos > info->cmp_size) + return -1U; + + if (max_cw_len > 32) + return -1U; + + if (max_cw_len == 0) + return read_pos; + + decoder = select_decoder(info->golomb_par_used); + if (!decoder) + return -1U; + + if (read_pos + max_cw_len > info->cmp_size) /* check buffer overflow */ + n_read_bits = info->cmp_size - read_pos; + else + n_read_bits = max_cw_len; + + read_bits = GetNBits32(&read_val, read_pos, n_read_bits, + compressed_data); + if (!read_bits) + return -1U; + + read_val = read_val << (32 - n_read_bits); + + log2_g = ilog_2(info->golomb_par_used); + n_bits = decoder(read_val, info->golomb_par_used, log2_g, decoded_val); + + return read_pos + n_bits; +} + + +static unsigned int decode_zero(const void *compressed_data, + const struct cmp_info *info, + unsigned int read_pos, unsigned int max_cw_len, + uint32_t *const decoded_val) +{ + if (!info) + return -1U; + + if (info->samples_used == 0) + return read_pos; + + if (!compressed_data) + return -1U; + + if (!decoded_val) + return -1U; + + if (read_pos > info->cmp_size) + return -1U; + + if (max_cw_len > 32) + return -1U; + + if (max_cw_len == 0) + return read_pos; + + read_pos = decode_normal(compressed_data, info, read_pos, max_cw_len, + decoded_val); + if (read_pos == -1U) + return -1U; + if (*decoded_val >= info->spill_used) /* consistency check */ + return -1U; + + if (*decoded_val == 0) {/* escape symbol mechanism was used; read unencoded value */ + unsigned int n_bits; + uint32_t unencoded_val; + + if (read_pos + max_cw_len > info->cmp_size) /* check buffer overflow */ + return -1U; + n_bits = GetNBits32(&unencoded_val, read_pos, max_cw_len, + compressed_data); + if (!n_bits) + return -1U; + if (unencoded_val < info->spill_used && unencoded_val != 0) /* consistency check */ + return -1U; + + *decoded_val = unencoded_val; + read_pos += n_bits; + } + return read_pos; +} + + +static unsigned int decode_multi(const void *compressed_data, + const struct cmp_info *info, + unsigned int read_pos, unsigned int max_cw_len, + uint32_t *const decoded_val) +{ + if (!info) + return -1U; + + if (info->samples_used == 0) + return read_pos; + + if (!compressed_data) + return -1U; + + if (!decoded_val) + return -1U; + + if (read_pos > info->cmp_size) + return -1U; + + if (max_cw_len > 32) + return -1U; + + if (max_cw_len == 0) + return read_pos; + + read_pos = decode_normal(compressed_data, info, read_pos, max_cw_len, + decoded_val); + if (read_pos == -1U) + return -1U; + + if (*decoded_val >= info->spill_used) { + /* escape symbol mechanism was used; read unencoded value */ + unsigned int n_bits; + uint32_t unencoded_val; + unsigned int unencoded_len; + + unencoded_len = (*decoded_val - info->spill_used + 1) * 2; + if (unencoded_len > max_cw_len) /* consistency check */ + return -1U; + + /* check buffer overflow */ + if ((read_pos + unencoded_len) > info->cmp_size) { + /*TODO: debug message */ + return -1U; + } + n_bits = GetNBits32(&unencoded_val, read_pos, unencoded_len, + compressed_data); + if (!n_bits) + return -1U; + + *decoded_val = unencoded_val + info->spill_used; + read_pos += n_bits; + } + return read_pos; +} + + +static unsigned int decode_value(const void *compressed_data, + const struct cmp_info *info, + unsigned int read_pos, + unsigned int max_cw_len, uint32_t *decoded_val) +{ + if (multi_escape_mech_is_used(info->cmp_mode_used)) + return decode_multi(compressed_data, info, read_pos, max_cw_len, + decoded_val); + + if (zero_escape_mech_is_used(info->cmp_mode_used)) + return decode_zero(compressed_data, info, read_pos, max_cw_len, + decoded_val); + return -1U; +} + + +static int decode_16(const void *compressed_data, const struct cmp_info *info, + uint16_t *decoded_data) +{ + size_t i; + unsigned int read_pos = 0; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decoded_data) + return -1; + + for (i = 0; i < info->samples_used; i++) { + uint32_t decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 16, + &decoded_val); + if (read_pos == -1U) { + debug_print("Error: Compressed values could not be decoded.\n"); + return -1; + } + + if (decoded_val > UINT16_MAX) + return -1; + + decoded_data[i] = (uint16_t)decoded_val; + } + + if (read_pos != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression.\n"); + return 1; + } + return 0; +} + + +static int decode_S_FX(const void *compressed_data, const struct cmp_info *info, + struct S_FX *decoded_data) +{ + size_t i; + unsigned int read_pos = 0; + struct cmp_info info_exp_flag = *info; + + info_exp_flag.golomb_par_used = GOLOMB_PAR_EXPOSURE_FLAGS; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decoded_data) + return -1; + + for (i = 0; i < info->samples_used; i++) { + uint32_t decoded_val; + + /* read_pos = decode_value(compressed_data, &info_exp_flag, read_pos, 8, */ + /* &decoded_val); */ + read_pos = decode_normal(compressed_data, &info_exp_flag, read_pos, 8, + &decoded_val); + if (read_pos == -1U) + return -1; + if (decoded_val > UINT8_MAX) + return -1; + decoded_data[i].EXPOSURE_FLAGS = (uint8_t)decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].FX = decoded_val; + } + + if (read_pos != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression."); + return 1; + } + return 0; +} + + +static int decode_S_FX_EFX(const void *compressed_data, const struct cmp_info + *info, struct S_FX_EFX *decoded_data) +{ + size_t i; + unsigned int read_pos = 0; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decoded_data) + return -1; + + for (i = 0; i < info->samples_used; i++) { + uint32_t decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 8, + &decoded_val); + if (read_pos == -1U) + return -1; + if (decoded_val > UINT8_MAX) + return -1; + decoded_data[i].EXPOSURE_FLAGS = (uint8_t)decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].FX = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].EFX = decoded_val; + } + + if (read_pos != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression.\n"); + return 1; + } + return 0; +} + + +static int decode_S_FX_NCOB(const void *compressed_data, const struct cmp_info + *info, struct S_FX_NCOB *decoded_data) +{ + size_t i; + unsigned int read_pos = 0; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decoded_data) + return -1; + + for (i = 0; i < info->samples_used; i++) { + uint32_t decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 8, + &decoded_val); + if (read_pos == -1U) + return -1; + if (decoded_val > UINT8_MAX) + return -1; + decoded_data[i].EXPOSURE_FLAGS = (uint8_t)decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].FX = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].NCOB_X = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].NCOB_Y = decoded_val; + } + + if (read_pos != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression.\n"); + return 1; + } + return 0; +} + + +static int decode_S_FX_EFX_NCOB_ECOB(const void *compressed_data, + const struct cmp_info *info, + struct S_FX_EFX_NCOB_ECOB *decoded_data) +{ + size_t i; + unsigned int read_pos = 0; + + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!decoded_data) + return -1; + + for (i = 0; i < info->samples_used; i++) { + uint32_t decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 8, + &decoded_val); + if (read_pos == -1U) + return -1; + if (decoded_val > UINT8_MAX) + return -1; + decoded_data[i].EXPOSURE_FLAGS = (uint8_t)decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].FX = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].NCOB_X = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].NCOB_Y = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].EFX = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].ECOB_X = decoded_val; + + read_pos = decode_value(compressed_data, info, read_pos, 32, + &decoded_val); + if (read_pos == -1U) + return -1; + decoded_data[i].ECOB_Y = decoded_val; + } + + if (read_pos != info->cmp_size) { + debug_print("Warning: The size of the decompressed bitstream does not match the size of the compressed bitstream. Check if the parameters used for decompression are the same as those used for compression."); + return 1; + } + return 0; +} + + +static int decode_data(const void *compressed_data, const struct cmp_info *info, + void *decompressed_data) +{ + if (!info) + return -1; + + if (info->samples_used == 0) + return 0; + + if (!compressed_data) + return -1; + + if (!decompressed_data) + return -1; + + switch (info->cmp_mode_used) { + case MODE_RAW: + return decode_raw_16(compressed_data, info, decompressed_data); + break; + case MODE_MODEL_ZERO: + case MODE_DIFF_ZERO: + case MODE_MODEL_MULTI: + case MODE_DIFF_MULTI: + return decode_16(compressed_data, info, + (uint16_t *)decompressed_data); + break; + case MODE_RAW_S_FX: + return decode_raw_S_FX(compressed_data, info, decompressed_data); + break; + case MODE_MODEL_ZERO_S_FX: + case MODE_MODEL_MULTI_S_FX: + case MODE_DIFF_ZERO_S_FX: + case MODE_DIFF_MULTI_S_FX: + return decode_S_FX(compressed_data, info, + (struct S_FX *)decompressed_data); + break; + case MODE_MODEL_ZERO_S_FX_EFX: + case MODE_MODEL_MULTI_S_FX_EFX: + case MODE_DIFF_ZERO_S_FX_EFX: + case MODE_DIFF_MULTI_S_FX_EFX: + return decode_S_FX_EFX(compressed_data, info, + (struct S_FX_EFX *)decompressed_data); + break; + case MODE_MODEL_ZERO_S_FX_NCOB: + case MODE_MODEL_MULTI_S_FX_NCOB: + case MODE_DIFF_ZERO_S_FX_NCOB: + case MODE_DIFF_MULTI_S_FX_NCOB: + return decode_S_FX_NCOB(compressed_data, info, + (struct S_FX_NCOB *)decompressed_data); + break; + case MODE_MODEL_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_MODEL_MULTI_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_ZERO_S_FX_EFX_NCOB_ECOB: + case MODE_DIFF_MULTI_S_FX_EFX_NCOB_ECOB: + return decode_S_FX_EFX_NCOB_ECOB(compressed_data, info, + (struct S_FX_EFX_NCOB_ECOB *) + decompressed_data); + break; +#if 0 + case MODE_MODEL_ZERO_F_FX: + case MODE_MODEL_MULTI_F_FX: + case MODE_DIFF_ZERO_F_FX: + case MODE_DIFF_MULTI_F_FX: + break; +#endif + default: + debug_print("Error: Compression mode not supported.\n"); + break; + + } + return -1; +} + +/* model buffer is overwritten with updated model*/ + +int decompress_data(const void *compressed_data, void *de_model_buf, const + struct cmp_info *info, void *decompressed_data) +{ + int err; + + if (!compressed_data) + return -1; + + if (!info) + return -1; + + if (info->cmp_err) + return -1; + + if (model_mode_is_used(info->cmp_mode_used)) + if (!de_model_buf) + return -1; + /* TODO: add ohter modes */ + + if (!decompressed_data) + return -1; + + + err = decode_data(compressed_data, info, decompressed_data); + if (err) + return err; + + err = de_map_to_pos(decompressed_data, info); + if (err) + return err; + + err = de_pre_process(decompressed_data, de_model_buf, info); + if (err) + return err; + + return 0; +} diff --git a/lib/rdcu_cmd.c b/lib/rdcu_cmd.c new file mode 100644 index 0000000..2154835 --- /dev/null +++ b/lib/rdcu_cmd.c @@ -0,0 +1,886 @@ +/** + * @file rdcu_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 RMAP RDCU RMAP command library + * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.7 + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <rmap.h> +#include <rdcu_cmd.h> +#include <rdcu_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 rdcu_read_cmd_register_internal(uint16_t trans_id, uint8_t *cmd, + uint32_t addr) +{ + return rdcu_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 rdcu_write_cmd_register_internal(uint16_t trans_id, uint8_t *cmd, + uint32_t addr) +{ + return rdcu_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 rdcu_write_cmd_data_internal(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size) +{ + return rdcu_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 rdcu_read_cmd_data_internal(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size) +{ + return rdcu_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 rdcu_write_cmd_data(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size) +{ + return rdcu_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 rdcu_read_cmd_data(uint16_t trans_id, uint8_t *cmd, + uint32_t addr, uint32_t size) +{ + return rdcu_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 rdcu_read_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr) +{ + return rdcu_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 rdcu_write_cmd_register(uint16_t trans_id, uint8_t *cmd, uint32_t addr) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, addr); +} + + + + +/** + * @brief create a command to read the RDCU FPGA version register + * + * @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 rdcu_read_cmd_fpga_version(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, FPGA_VERSION); +} + + +/** + * @brief create a command to read the RDCU status register + * + * @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 rdcu_read_cmd_rdcu_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, RDCU_STATUS); +} + + +/** + * @brief create a command to read the RDCU LVDS core status register + * + * @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 rdcu_read_cmd_lvds_core_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, LVDS_CORE_STATUS); +} + + +/** + * @brief create a command to read the RDCU SPW link status register + * + * @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 rdcu_read_cmd_spw_link_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, SPW_LINK_STATUS); +} + + +/** + * @brief create a command to read the RDCU SPW error counters register + * + * @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 rdcu_read_cmd_spw_err_cntrs(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, SPW_ERR_CNTRS); +} + + +/** + * @brief create a command to read the RDCU RMAP last error register + * + * @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 rdcu_read_cmd_rmap_last_err(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, RMAP_LAST_ERR); +} + + +/** + * @brief create a command to read the RDCU RMAP no reply error counter register + * + * @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 rdcu_read_cmd_rmap_no_reply_err_cntrs(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + RMAP_NO_REPLY_ERR_CNTRS); +} + + +/** + * @brief create a command to read the RDCU RMAP packet error counter register + * + * @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 rdcu_read_cmd_rmap_pckt_err_cntrs(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + RMAP_PCKT_ERR_CNTRS); +} + + +/** + * @brief create a command to read the RDCU ADC values 1 register + * + * @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 rdcu_read_cmd_adc_values_1(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, ADC_VALUES_1); +} + + +/** + * @brief create a command to read the RDCU ADC values 2 register + * + * @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 rdcu_read_cmd_adc_values_2(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, ADC_VALUES_2); +} + + +/** + * @brief create a command to read the RDCU ADC values 3 register + * + * @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 rdcu_read_cmd_adc_values_3(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, ADC_VALUES_3); +} + + +/** + * @brief create a command to read the RDCU ADC values 4 register + * + * @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 rdcu_read_cmd_adc_values_4(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, ADC_VALUES_4); +} + + +/** + * @brief create a command to read the RDCU ADC status register + * + * @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 rdcu_read_cmd_adc_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, ADC_STATUS); +} + + +/** + * @brief create a command to read the RDCU compressor status register + * + * @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 rdcu_read_cmd_compr_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, COMPR_STATUS); +} + + + +/** + * @brief create a command to write the RDCU reset register + * + * @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 rdcu_write_cmd_rdcu_reset(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, RDCU_RESET); +} + + +/** + * @brief create a command to write the RDCU SPW link control register + * + * @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 rdcu_write_cmd_spw_link_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, SPW_LINK_CTRL); +} + + +/** + * @brief create a command to write the RDCU LVDS control register + * + * @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 rdcu_write_cmd_lvds_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, LVDS_CTRL); +} + + +/** + * + * @brief create a command to write the RDCU core control register + * + * @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 rdcu_write_cmd_core_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, CORE_CTRL); +} + + +/** + * + * @brief create a command to write the RDCU ADC control register + * + * @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 rdcu_write_cmd_adc_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, ADC_CTRL); +} + + +/** + * + * @brief create a command to write the RDCU compressor control register + * + * @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 rdcu_write_cmd_compr_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, COMPR_CTRL); +} + + +/** + * @brief create a command to write the RDCU compressor parameter 1 register + * + * @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 rdcu_write_cmd_compressor_param1(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, COMPR_PARAM_1); +} + + +/** + * @brief create a command to write the RDCU compressor parameter 2 register + * + * @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 rdcu_write_cmd_compressor_param2(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, COMPR_PARAM_2); +} + + +/** + * @brief create a command to write the RDCU adaptive parameter 1 register + * + * @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 rdcu_write_cmd_adaptive_param1(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, + ADAPTIVE_PARAM_1); +} + + +/** + * @brief create a command to write the RDCU adaptive parameter 2 register + * + * @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 rdcu_write_cmd_adaptive_param2(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, + ADAPTIVE_PARAM_2); +} + + +/** + * @brief create a command to write the RDCU data start address register + * + * @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 rdcu_write_cmd_data_start_addr(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, DATA_START_ADDR); +} + + +/** + * @brief create a command to write the RDCU model start address register + * + * @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 rdcu_write_cmd_model_start_addr(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, MODEL_START_ADDR); +} + + +/** + * @brief create a command to write the RDCU number of samples register + * + * @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 rdcu_write_cmd_num_samples(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, NUM_SAMPLES); +} + + +/** + * @brief create a command to write the RDCU updated/new model start address register + * + * @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 rdcu_write_cmd_new_model_start_addr(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, + UPDATED_MODEL_START_ADDR); +} + + +/** + * @brief create a command to write the RDCU compressed data buffer start + * address register + * + * @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 rdcu_write_cmd_compr_data_buf_start_addr(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, + COMPR_DATA_BUF_START_ADDR); +} + + +/** + * @brief create a command to write the RDCU compressed data buffer length + * register + * + * @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 rdcu_write_cmd_compr_data_buf_len(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, + COMPR_DATA_BUF_LEN); +} + + + +/** + * @brief create a command to read the RDCU used paramter 1 register + * + * @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 rdcu_read_cmd_used_param1(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + USED_COMPR_PARAM_1); +} + + +/** + * @brief create a command to read the RDCU unused paramter 2 register + * + * @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 rdcu_read_cmd_used_param2(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + USED_COMPR_PARAM_2); +} + + +/** + * @brief create a command to read the RDCU compressed data start address + * register + * + * @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 rdcu_read_cmd_compr_data_start_addr(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + COMPR_DATA_START_ADDR); +} + + +/** + * @brief create a command to read the RDCU compressed data size register + * + * @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 rdcu_read_cmd_compr_data_size(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, COMPR_DATA_SIZE); +} + + +/** + * @brief create a command to read the RDCU compressed data adaptive 1 size + * register + * + * @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 rdcu_read_cmd_compr_data_adaptive_1_size(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + COMPR_DATA_ADAPTIVE_1_SIZE); +} + + +/** + * @brief create a command to read the RDCU compressed data adaptive 2 size + * register + * + * @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 rdcu_read_cmd_compr_data_adaptive_2_size(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + COMPR_DATA_ADAPTIVE_2_SIZE); +} + + +/** + * @brief create a command to read the RDCU compression error register + * + * @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 rdcu_read_cmd_compr_error(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, COMPR_ERROR); +} + + +/** + * @brief create a command to read the RDCU updated model start address register + * + * @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 rdcu_read_cmd_new_model_addr_used(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + USED_UPDATED_MODEL_START_ADDR); +} + + +/** + * @brief create a command to read the RDCU used number of samples register + * + * @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 rdcu_read_cmd_samples_used(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, + USED_NUMBER_OF_SAMPLES); +} + + +/** + * @brief create a command to write the SRAM EDAC Control register + * + * @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 rdcu_write_cmd_sram_edac_ctrl(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_write_cmd_register_internal(trans_id, cmd, SRAM_EDAC_CTRL); +} + +/** + * @brief create a command to read the SRAM EDAC Status register + * + * @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 rdcu_read_cmd_sram_edac_status(uint16_t trans_id, uint8_t *cmd) +{ + return rdcu_read_cmd_register_internal(trans_id, cmd, SRAM_EDAC_STATUS); +} diff --git a/lib/rdcu_ctrl.c b/lib/rdcu_ctrl.c new file mode 100644 index 0000000..a1e3b0f --- /dev/null +++ b/lib/rdcu_ctrl.c @@ -0,0 +1,2491 @@ +/** + * @file rdcu_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 RDCU control library + * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.7 + * + * USAGE: + * All sync() calls respect the direction of the sync, i.e. read-only + * registers in the RDCU are synced to the local mirror + * and vice-versa for write-only register + * The only exception are SRAM (data) related syncs, they specifiy + * the direction by a directional suffix, which is either _icu_rdcu + * for ICU->RDCU, i.e. transfer local to remote, or _rdcu_icu for a + * read-back + * + * Access to the local mirror is provided by _get or _set calls, so + * to configure a register in the RDCU, one would call the sequence: + * + * set_register_xyz(someargument); + * sync_register_xyz(); + * + * while(rdcu_sync_status()) + * wait_busy_or_do_something_else_your_choice(); + * + * [sync_comple] + * + * and to read a register: + * + * sync_register_uvw() + * + * while(rdcu_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 + * In the PLATO-IWF-PL-RS-005 Issue 0.7 (at least the one I have), + * register bits in tables and register figures are sometimes in + * conflict. Only values from tables have been used here. + */ + + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include <byteorder.h> +#include <rmap.h> +#include <rdcu_cmd.h> +#include <rdcu_ctrl.h> +#include <rdcu_rmap.h> + + +static struct rdcu_mirror *rdcu; + + +/** + * @brief get the 4 FPGA minor/major version digits + * @see RDCU-FRS-FN-0522 + */ + +uint16_t rdcu_get_fpga_version(void) +{ + return (uint16_t) (rdcu->fpga_version & 0xFFFF); +} + + +/** + * @brief get the RDCU board serial number + * @see RDCU-FRS-FN-0532 + */ + +uint32_t rdcu_get_rdcu_status_board_serial_number(void) +{ + return (rdcu->rdcu_status >> 12) & 0xFUL; +} + + +/** + * @brief get FPGA core power good bit + * @see RDCU-FRS-FN-0532 + * + * @returns 0 : FPGA core power supply failure + * 1 : FPGA core power supply (1.5V) is good + */ + +uint32_t rdcu_get_rdcu_status_fpga_core_power_good(void) +{ + return (rdcu->rdcu_status >> 6) & 0x1UL; +} + + +/** + * @brief get core power good bit + * @see RDCU-FRS-FN-0532 + * + * @returns 0 : FPGA core power supply failure + * 1 : FPGA core power supply (1.8V) is good + */ + +uint32_t rdcu_get_rdcu_status_core_power_good(void) +{ + return (rdcu->rdcu_status >> 5) & 0x1UL; +} + + +/** + * @brief get i/o power good bit + * @see RDCU-FRS-FN-0532 + * + * @returns 0 : FPGA core power supply failure + * 1 : FPGA core power supply (3.3V) is good + */ + +uint32_t rdcu_get_rdcu_status_io_power_good(void) +{ + return (rdcu->rdcu_status >> 4) & 0x1UL; +} + + +/** + * @brief get reset by register bit + * @see RDCU-FRS-FN-0532 + * + * @returns 0 : not the reset reason + * 1 : reset triggered by register bit + */ + +uint32_t rdcu_get_rdcu_status_reset_by_register(void) +{ + return (rdcu->rdcu_status >> 1) & 0x1UL; +} + + +/** + * @brief get power on reset bit + * @see RDCU-FRS-FN-0532 + * + * @returns 0 : not the reset reason + * 1 : reset triggered by power on + */ + +uint32_t rdcu_get_rdcu_status_power_on_reset(void) +{ + return rdcu->rdcu_status & 0x1UL; +} + + +/** + * @brief get RMAP Target logical address + * @see RDCU-FRS-FN-0542 + */ + +uint8_t rdcu_get_rmap_target_logical_address(void) +{ + return (uint8_t) ((rdcu->lvds_core_status >> 24) & 0xFFUL); +} + + +/** + * @brief get RMAP Target command key + * @see RDCU-FRS-FN-0542 + */ + +uint8_t rdcu_get_rmap_target_cmd_key(void) +{ + return (uint8_t) ((rdcu->lvds_core_status >> 16) & 0xFFUL); +} + + +/** + * @brief get LVDS link enabled bit + * + * @param link the link number (0-7) + * + * @see RDCU-FRS-FN-0542 + * + * @returns 0 : LVDS link disabled + * 1 : LVDS link enabled + * other: invalid link argument + */ + +uint32_t rdcu_get_lvds_link_enabled(uint32_t link) +{ + if (link > 7) + return -1; + + return (rdcu->lvds_core_status >> link) & 0x1UL; +} + + +/** + * @brief get SpW empty packet count + * @see RDCU-FRS-FN-0552 + */ + +uint16_t rdcu_get_spw_empty_pckt_cnt(void) +{ + return (uint16_t) ((rdcu->spw_link_status >> 16) & 0xFFUL); +} + + +/** + * @brief get SpW run-state clock divisor value + * @see RDCU-FRS-FN-0552 + */ + +uint8_t rdcu_get_spw_run_clk_div(void) +{ + return (uint8_t) ((rdcu->spw_link_status >> 8) & 0x3FUL); +} + + +/** + * @brief get SpW link run state + * @see RDCU-FRS-FN-0552 + * + * @returns 0 : SpW link not established + * 1 : SpW link is running + */ + +uint8_t rdcu_get_spw_lnk_run_state(void) +{ + return (uint8_t) ((rdcu->spw_link_status >> 8) & 0x1UL); +} + + +/** + * @brief get SpW link credit errors + * @see RDCU-FRS-FN-0562 + */ + +uint8_t rdcu_get_spw_lnk_credit_errs(void) +{ + return (uint8_t) ((rdcu->spw_link_status >> 24) & 0xFFUL); +} + + +/** + * @brief get SpW link escape errors + * @see RDCU-FRS-FN-0562 + */ + +uint8_t rdcu_get_spw_lnk_escape_errs(void) +{ + return (uint8_t) ((rdcu->spw_link_status >> 16) & 0xFFUL); +} + + +/** + * @brief get SpW link parity errors + * @see RDCU-FRS-FN-0562 + */ + +uint8_t rdcu_get_spw_lnk_parity_errs(void) +{ + return (uint8_t) ((rdcu->spw_link_status >> 8) & 0xFFUL); +} + + +/** + * @brief get SpW link disconnect errors + * @see RDCU-FRS-FN-0562 + */ + +uint8_t rdcu_get_spw_lnk_disconnect_errs(void) +{ + return (uint8_t) (rdcu->spw_link_status & 0xFFUL); +} + + +/** + * @brief get RMAP last error user code + * @see RDCU-FRS-FN-0572 + */ + +uint8_t rdcu_get_rmap_last_error_user_code(void) +{ + return (uint8_t) ((rdcu->rmap_last_err >> 8) & 0xFFUL); +} + + +/** + * @brief get RMAP last error standard code + * @see RDCU-FRS-FN-0572 + */ + +uint8_t rdcu_get_rmap_last_error_standard_code(void) +{ + return (uint8_t) (rdcu->rmap_last_err & 0xFFUL); +} + + +/** + * @brief get RMAP incomplete header error counter + * @see RDCU-FRS-FN-0582 + */ + +uint8_t rdcu_get_rmap_incomplete_hdrs(void) +{ + return (uint8_t) ((rdcu->rmap_no_reply_err_cntrs >> 24) & 0xFFUL); +} + + +/** + * @brief get RMAP received reply packet counter + * @see RDCU-FRS-FN-0582 + */ + +uint8_t rdcu_get_rmap_recv_reply_pckts(void) +{ + return (uint8_t) ((rdcu->rmap_no_reply_err_cntrs >> 8) & 0xFFUL); +} + + +/** + * @brief get received non-RMAP packet counter + * @see RDCU-FRS-FN-0582 + */ + +uint8_t rdcu_get_recv_non_rmap_pckts(void) +{ + return (uint8_t) (rdcu->rmap_no_reply_err_cntrs & 0xFFUL); +} + + +/** + * @brief get RMAP packet with length or content error counter + * @see RDCU-FRS-FN-0592 + */ + +uint8_t rdcu_get_rmap_pckt_errs(void) +{ + return (uint8_t) ((rdcu->rmap_pckt_err_cntrs >> 24) & 0xFFUL); +} + + +/** + * @brief get RMAP operation error counter + * @see RDCU-FRS-FN-0592 + */ + +uint8_t rdcu_get_rmap_oper_errs(void) +{ + return (uint8_t) ((rdcu->rmap_pckt_err_cntrs >> 16) & 0xFFUL); +} + + +/** + * @brief get RMAP command authorization error counter + * @see RDCU-FRS-FN-0592 + */ + +uint8_t rdcu_get_rmap_cmd_auth_errs(void) +{ + return (uint8_t) ((rdcu->rmap_pckt_err_cntrs >> 8) & 0xFFUL); +} + + +/** + * @brief get RMAP header error counter + * @see RDCU-FRS-FN-0592 + */ + +uint8_t rdcu_get_rmap_hdr_errs(void) +{ + return (uint8_t) (rdcu->rmap_pckt_err_cntrs & 0xFFUL); +} + + +/** + * @brief get an ADC value + * @param id the ADC value id to get (1-8) + * @see RDCU-FRS-FN-0602 + * + * @return adc value, if 0xFFFF, id may be invalid + */ + +uint16_t rdcu_get_adc_value(int id) +{ + switch (id) { + case 1: + return (uint16_t) (rdcu->adc_values_1 & 0xFFFFUL); + case 2: + return (uint16_t) ((rdcu->adc_values_1 >> 16) & 0xFFFFUL); + case 3: + return (uint16_t) (rdcu->adc_values_2 & 0xFFFFUL); + case 4: + return (uint16_t) ((rdcu->adc_values_2 >> 16) & 0xFFFFUL); + case 5: + return (uint16_t) (rdcu->adc_values_3 & 0xFFFFUL); + case 6: + return (uint16_t) ((rdcu->adc_values_3 >> 16) & 0xFFFFUL); + case 7: + return (uint16_t) (rdcu->adc_values_4 & 0xFFFFUL); + case 8: + return (uint16_t) ((rdcu->adc_values_4 >> 16) & 0xFFFFUL); + + default: + break; + } + + return 0xFFFF; +} + + +/** + * @brief get valid ADC values + * @see RDCU-FRS-FN-0612 + * + * @returns 0: no valid ADC values in the ADC value registers + * 1: the ADC value registers contain valid ADC results + */ + +uint32_t rdcu_get_valid_adc_values(void) +{ + return ((rdcu->adc_status >> 4) & 0x1UL); +} + + +/** + * @brief get ADC logic reset + * @see RDCU-FRS-FN-0612 + * + * @returns 0: normal ADC operation + * 1: ADC logic is in reset state + */ + +uint32_t rdcu_get_adc_logic_reset(void) +{ + return ((rdcu->adc_status >> 1) & 0x1UL); +} + + +/** + * @brief get ADC logic enabled + * @see RDCU-FRS-FN-0612 + * + * @returns 0: ADC logic disabled + * 1: ADC logic enabled (normal operation) + */ + +uint32_t rdcu_get_adc_logic_enabled(void) +{ + return (rdcu->adc_status & 0x1UL); +} + + +/** + * @brief get RDCU Interrupt status + * @see RDCU-FRS-FN-0632 + * + * @returns 0: Interrupt is disabled + * 1: Interrupt is enabled + */ + +uint32_t rdcu_get_rdcu_interrupt_enabled(void) +{ + return ((rdcu->compr_status >> 8) & 0x1UL); +} + + +/** + * @brief get compressor status valid + * @see RDCU-FRS-FN-0632 + * + * @returns 0: Data is invalid + * 1: Data is valid + */ + +uint32_t rdcu_get_compr_status_valid(void) +{ + return ((rdcu->compr_status >> 5) & 0x1UL); +} + + +/** + * @brief get data compressor ready + * @see RDCU-FRS-FN-0632 + * + * @returns 0: Compressor is busy + * 1: Compressor is ready + */ + +uint32_t rdcu_get_data_compr_ready(void) +{ + return ((rdcu->compr_status >> 4) & 0x1UL); +} + + +/** + * @brief get data compressor interrupted + * @see RDCU-FRS-FN-0632 + * + * @returns 0: No compressor interruption + * 1: Compressor was interrupted + */ + +uint32_t rdcu_get_data_compr_interrupted(void) +{ + return ((rdcu->compr_status >> 1) & 0x1UL); +} + + +/** + * @brief get data compressor activce + * @see RDCU-FRS-FN-0632 + * + * @returns 0: Compressor is on hold + * 1: Compressor is active + */ + +uint32_t rdcu_get_data_compr_active(void) +{ + return (rdcu->compr_status & 0x1UL); +} + + +/** + * @brief set RDCU Board Reset Keyword + * @see RDCU-FRS-FN-0662 + * + * @note the valid key is 0x9A + */ + +void rdcu_set_rdcu_board_reset_keyword(uint8_t key) +{ + /* clear and set */ + rdcu->rdcu_reset &= ~(0xFUL << 24); + rdcu->rdcu_reset |= ((uint32_t) key << 24); +} + + +/** + * @brief set RDCU Internal Bus Reset bit + * @see RDCU-FRS-FN-0662 + * + * @note The bit will auto-clear in the FPGA, to clear the local mirror, + * make sure to rdcu_clear_rdcu_bus_reset() so the FPGA internal bus + * is not reset every time rdcu_sync_rdcu_reset() is called, as + * write-only registers are note synced back from the RDCU + */ + +void rdcu_set_rdcu_bus_reset(void) +{ + rdcu->rdcu_reset |= (0x1UL << 12); +} + + +/** + * @brief clear RDCU Internal Bus Reset bit + * @see RDCU-FRS-FN-0662 + */ + +void rdcu_clear_rdcu_bus_reset(void) +{ + rdcu->rdcu_reset &= ~(0x1UL << 12); +} + + +/** + * @brief set RDCU RMAP error counter Reset bit + * @see RDCU-FRS-FN-0662 + * + * @note The bit will auto-clear in the FPGA, to clear the local mirror, + * make sure to rdcu_clear_rdcu_rmap_error_cntr_reset() so the FPGA + * does not reset the RMAP error counter every time rdcu_sync_rdcu_reset() + * is called, as write-only registers are not synced back from the RDCU. + */ + +void rdcu_set_rdcu_rmap_error_cntr_reset(void) +{ + rdcu->rdcu_reset |= (0x1UL << 9); +} + + +/** + * @brief clear RDCU RMAP error counter Reset bit + * @see RDCU-FRS-FN-0662 + */ + +void rdcu_clear_rdcu_rmap_error_cntr_reset(void) +{ + rdcu->rdcu_reset &= ~(0x1UL << 9); +} + + +/** + * @brief set RDCU SpaceWire error counter Reset bit + * @see RDCU-FRS-FN-0662 + * + * @note The bit will auto-clear in the FPGA, to clear the local mirror, + * make sure to rdcu_clear_rdcu_spw_error_cntr_reset() so the FPGA + * does not reset the SpW error counter every time rdcu_sync_rdcu_reset() + * is called, as write-only registers are not synced back from the RDCU. + */ + +void rdcu_set_rdcu_spw_error_cntr_reset(void) +{ + rdcu->rdcu_reset |= (0x1UL << 8); +} + + +/** + * @brief clear RDCU SpaceWire error counter Reset bit + * @see RDCU-FRS-FN-0662 + */ + +void rdcu_clear_rdcu_spw_error_cntr_reset(void) +{ + rdcu->rdcu_reset &= ~(0x1UL << 8); +} + + +/** + * @brief set RDCU Board Reset bit + * @see RDCU-FRS-FN-0662 + * + * @note The bit will auto-clear in the FPGA, to clear the local mirror, + * make sure to rdcu_clear_rdcu_board_reset() so the FPGA + * does not reset the SpW error counter every time rdcu_sync_rdcu_reset() + * is called, as write-only registers are not synced back from the RDCU. + */ + +void rdcu_set_rdcu_board_reset(void) +{ + rdcu->rdcu_reset |= (0x1UL << 1); +} + + +/** + * @brief clear RDCU SpaceWire error counter Reset bit + * @see RDCU-FRS-FN-0662 + */ + +void rdcu_clear_rdcu_board_reset(void) +{ + rdcu->rdcu_reset &= ~(0x1UL << 1); +} + + +/** + * @brief set SpW Link Control Run-State Clock Divisor + * @see RDCU-FRS-FN-0672 + * + * @note value is scaling factor minus 1 + */ + +int rdcu_set_spw_link_run_clkdiv(uint8_t div) +{ + if (div > 49) + return -1; + + /* clear and set */ + rdcu->spw_link_ctrl &= ~(0x3FUL << 8); + rdcu->spw_link_ctrl |= ((uint32_t) div << 8); + + return 0; +} + + +/** + * @brief set LVDS link enabled + * + * @param link the link number (0-7) + * + * @see RDCU-FRS-FN-0682 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_lvds_link_enabled(uint32_t link) +{ + if (link > 7) + return -1; + + rdcu->lvds_ctrl |= (0x1UL << link); + + return 0; +} + + +/** + * @brief set LVDS link disabled + * + * @param link the link number (0-7) + * + * @see RDCU-FRS-FN-0682 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_lvds_link_disabled(uint32_t link) +{ + if (link > 7) + return -1; + + rdcu->lvds_ctrl &= ~((0x1UL << link)); + + return 0; +} + + +/** + * @brief set RMAP Target logical address + * @see RDCU-FRS-FN-0692 + */ + +void rdcu_set_rmap_target_logical_address(uint8_t addr) +{ + rdcu->core_ctrl &= ~(0xFFUL << 24); + rdcu->core_ctrl |= ((uint32_t) addr) << 24; +} + + +/** + * @brief set RMAP Target command key + * @see RDCU-FRS-FN-0692 + */ + +void rdcu_set_rmap_target_cmd_key(uint8_t key) +{ + rdcu->core_ctrl &= ~(0xFFUL << 16); + rdcu->core_ctrl |= ((uint32_t) key) << 16; +} + + +/** + * @brief set the ADC logic reset bit + * @see RDCU-FRS-FN-0712 + * + * @note use rdcu_clear_adc_logic_reset(), rdcu_sync_adc_ctrl() sequence + * to clear and start normal operation + */ + +void rdcu_set_adc_logic_reset(void) +{ + rdcu->adc_ctrl |= (0x1UL << 1); +} + + +/** + * @brief clear the ADC logic reset bit + * @see RDCU-FRS-FN-0712 + */ + +void rdcu_clear_adc_logic_reset(void) +{ + rdcu->adc_ctrl &= ~(0x1UL << 1); +} + + +/** + * @brief set the ADC logic enabled + * @see RDCU-FRS-FN-0712 + */ + +void rdcu_set_adc_logic_enabled(void) +{ + rdcu->adc_ctrl |= 0x1UL; +} + + +/** + * @brief set the ADC logic disabled + * @see RDCU-FRS-FN-0712 + */ + +void rdcu_set_adc_logic_disabled(void) +{ + rdcu->adc_ctrl &= ~0x1UL; +} + + +/** + * @brief enable RDCU interrupt signal to the ICU + * @see RDCU-FRS-FN-0732 + */ + +void rdcu_set_rdcu_interrupt(void) +{ + rdcu->compr_ctrl |= (0x1UL << 8); +} + + +/** + * @brief disable RDCU interrupt signal to the ICU + * @see RDCU-FRS-FN-0732 + */ + +void rdcu_clear_rdcu_interrupt(void) +{ + rdcu->compr_ctrl &= ~(0x1UL << 8); +} + + +/** + * @brief set data compressor interrupt + * @see RDCU-FRS-FN-0732 + * + * @note The bit will auto-clear in the FPGA once compression is complete. + * To clear the local mirror, make sure to + * rdcu_clear_data_compr_interrupt() so the FPGA does not interrupt + * data compression unexepectedly when rdcu_sync_compr_ctrl() + * is called, as write-only registers are not synced back from the RDCU. + * + */ + +void rdcu_set_data_compr_interrupt(void) +{ + rdcu->compr_ctrl |= (0x1UL << 1); +} + + +/** + * @brief clear data compressor interrupt + * @see RDCU-FRS-FN-0732 + */ + +void rdcu_clear_data_compr_interrupt(void) +{ + rdcu->compr_ctrl &= ~(0x1UL << 1); +} + + +/** + * @brief set data compressor start bit + * @see RDCU-FRS-FN-0732 + * + * @note The bit will auto-clear in the FPGA once compression is complete. + * To clear the local mirror, make sure to + * rdcu_clear_data_compr_start() so the FPGA does not start + * data compression unexepectedly when rdcu_sync_compr_ctrl() + * is called, as write-only registers are not synced back from the RDCU. + */ + +void rdcu_set_data_compr_start(void) +{ + rdcu->compr_ctrl |= 0x1UL; +} + + +/** + * @brief clear data compressor start bit + * @see RDCU-FRS-FN-0732 + */ + +void rdcu_clear_data_compr_start(void) +{ + rdcu->compr_ctrl &= ~0x1UL; +} + + +/** + * @brief set number of noise bits to be rounded + * @see RDCU-FRS-FN-0772 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_noise_bits_rounded(uint32_t rpar) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (rpar > 3) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->compressor_param1 &= ~(0x3UL << 16); + rdcu->compressor_param1 |= (rpar << 16); + + return 0; +} + + +/** + * @brief set weighting parameter + * @see RDCU-FRS-FN-0772 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_weighting_param(uint32_t mval) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (mval > 16) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->compressor_param1 &= ~(0x1FUL << 8); + rdcu->compressor_param1 |= (mval << 8); + + return 0; +} + + +/** + * @brief set compression mode + * @see RDCU-FRS-FN-0772 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_compression_mode(uint32_t cmode) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (cmode > 4) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->compressor_param1 &= ~0xFFUL; + rdcu->compressor_param1 |= cmode; + + return 0; +} + + +/** + * @brief set spillover threshold for encoding outliers + * @see RDCU-FRS-FN-0782 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_spillover_threshold(uint32_t spill) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (spill < 2) + return -1; + + if (spill > 16383) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->compressor_param2 &= ~(0x3FFFUL << 8); + rdcu->compressor_param2 |= (spill << 8); + + return 0; +} + + +/** + * @brief set Golomb parameter for dictionary selection + * @see RDCU-FRS-FN-0782 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_golomb_param(uint32_t gpar) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (!gpar) + return -1; + + if (gpar > 63) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->compressor_param2 &= ~0x3FUL; + rdcu->compressor_param2 |= gpar; + + return 0; +} + + +/** + * @brief set adaptive 1 spillover threshold for encoding outliers + * @see RDCU-FRS-FN-0792 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_adaptive_1_spillover_threshold(uint32_t spill) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (spill < 2) + return -1; + + if (spill > 16383) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->adaptive_param1 &= ~(0x3FFFUL << 8); + rdcu->adaptive_param1 |= (spill << 8); + + return 0; +} + + +/** + * @brief set adaptive 1 Golomb parameter for dictionary selection + * @see RDCU-FRS-FN-0792 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_adaptive_1_golomb_param(uint32_t gpar) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (!gpar) + return -1; + + if (gpar > 63) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->adaptive_param1 &= ~0x3FUL; + rdcu->adaptive_param1 |= gpar; + + return 0; +} + + + +/** + * @brief set adaptive 2 spillover threshold for encoding outliers + * @see RDCU-FRS-FN-0802 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_adaptive_2_spillover_threshold(uint32_t spill) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (spill < 2) + return -1; + + if (spill > 16383) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->adaptive_param2 &= ~(0x3FFFUL << 8); + rdcu->adaptive_param2 |= (spill << 8); + + return 0; +} + + +/** + * @brief set adaptive 2 Golomb parameter for dictionary selection + * @see RDCU-FRS-FN-0802 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_adaptive_2_golomb_param(uint32_t gpar) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (!gpar) + return -1; + + if (gpar > 63) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->adaptive_param2 &= ~0x3FUL; + rdcu->adaptive_param2 |= gpar; + + return 0; +} + + +/** + * @brief set data start address + * @see RDCU-FRS-FN-0812 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_data_start_addr(uint32_t addr) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (addr > 0x00FFFFFFUL) + return -1; + + if (addr & 0x3) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->data_start_addr &= ~0x00FFFFFFUL; + rdcu->data_start_addr |= addr; + + return 0; +} + + +/** + * @brief set model start address + * @see RDCU-FRS-FN-0822 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_model_start_addr(uint32_t addr) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (addr > 0x00FFFFFFUL) + return -1; + + if (addr & 0x3) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->model_start_addr &= ~0x00FFFFFFUL; + rdcu->model_start_addr |= addr; + + return 0; +} + + +/** + * @brief set number of data samples (16 bit values) to compress + * @see RDCU-FRS-FN-0832 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_num_samples(uint32_t samples) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (samples > 0x00FFFFFFUL) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->num_samples &= ~0x00FFFFFFUL; + rdcu->num_samples |= samples; + + return 0; +} + + +/** + * @brief set updated_model/new model start address + * @see RDCU-FRS-FN-0842 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_new_model_start_addr(uint32_t addr) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (addr > 0x00FFFFFFUL) + return -1; + + if (addr & 0x3) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + + /* clear and set */ + rdcu->new_model_start_addr &= ~0x00FFFFFFUL; + rdcu->new_model_start_addr |= addr; + + return 0; +} + + +/** + * @brief set compressed data buffer start address + * @see RDCU-FRS-FN-0850 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_compr_data_buf_start_addr(uint32_t addr) +{ +#ifndef SKIP_CMP_PAR_CHECK + if (addr > 0x00FFFFFFUL) + return -1; + + if (addr & 0x3) + return -1; +#endif /*SKIP_CMP_PAR_CHECK*/ + /* clear and set */ + rdcu->compr_data_buf_start_addr &= ~0x00FFFFFFUL; + rdcu->compr_data_buf_start_addr |= addr; + + return 0; +} + + +/** + * @brief set compressed data buffer length (in 16 bit values) + * @see RDCU-FRS-FN-0862 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_set_compr_data_buf_len(uint32_t samples) +{ + if (samples > 0x00FFFFFFUL) + return -1; + + /* clear and set */ + rdcu->compr_data_buf_len &= ~0x00FFFFFFUL; + rdcu->compr_data_buf_len |= samples; + + return 0; +} + + +/** + * @brief get compression mode + * @see RDCU-FRS-FN-0892 + * + * @returns the CMODE + */ + +uint32_t rdcu_get_compression_mode(void) +{ + return rdcu->used_param1 & 0xFFUL; +} + + +/** + * @brief get number of noise bits to be rounded + * @see RDCU-FRS-FN-0892 + * + * @returns the RPAR + */ + +uint32_t rdcu_get_noise_bits_rounded(void) +{ + return (rdcu->used_param1 >> 16) & 0x3UL; +} + + +/** + * @brief get weighting parameter + * @see RDCU-FRS-FN-0892 + * + * @returns the RPAR + */ + +uint32_t rdcu_get_weighting_param(void) +{ + return (rdcu->used_param1 >> 8) & 0x1FUL; +} + + +/** + * @brief get spillover threshold for encoding outliers + * @see RDCU-FRS-FN-0902 + * + * @returns the SPILL + */ + +uint32_t rdcu_get_spillover_threshold(void) +{ + return (rdcu->used_param2 >> 8) & 0x3FFFUL; +} + + +/** + * @brief get spillover threshold for encoding outliers + * @see RDCU-FRS-FN-0902 + * + * @returns the GPAR + */ + +uint32_t rdcu_get_golomb_param(void) +{ + return rdcu->used_param2 & 0x3FUL; +} + + +/** + * @brief get compressed data start address + * @see RDCU-FRS-FN-0912 + * + * @returns the output SRAM address + */ + +uint32_t rdcu_get_compr_data_start_addr(void) +{ + return rdcu->compr_data_start_addr & 0x00FFFFFFUL; +} + + +/** + * @brief get compressed data size + * @see RDCU-FRS-FN-0922 + * + * @returns the compressed data size in bits + */ + +uint32_t rdcu_get_compr_data_size(void) +{ + return rdcu->compr_data_size; +} + + +/** + * @brief get compressed data adaptive 1 size + * @see RDCU-FRS-FN-0932 + * + * @returns the adaptive 1 compressed data size in bits + */ + +uint32_t rdcu_get_compr_data_adaptive_1_size(void) +{ + return rdcu->compr_data_adaptive_1_size; +} + + +/** + * @brief get compressed data adaptive 2 size + * @see RDCU-FRS-FN-0942 + * + * @returns the adaptive 2 compressed data size in bits + */ + +uint32_t rdcu_get_compr_data_adaptive_2_size(void) +{ + return rdcu->compr_data_adaptive_2_size; +} + + +/** + * @brief get compression error code + * @see RDCU-FRS-FN-0954 + * + * @returns the compression error code + */ + +uint16_t rdcu_get_compr_error(void) +{ + return (uint16_t) (rdcu->compr_error & 0x3FFUL); +} + + +/** + * @brief get model info start address + * @see RDCU-FRS-FN-0960 + * + */ + +uint32_t rdcu_get_new_model_addr_used(void) +{ + return rdcu->new_model_addr_used & 0x00FFFFFFUL; +} + + +/** + * @brief get model info length + * @see RDCU-FRS-FN-0972 + * + * @returns the number of 16-bit samples in the model + */ + +uint32_t rdcu_get_samples_used(void) +{ + return rdcu->samples_used & 0x00FFFFFFUL; +} + + +/** + * @brief set EDAC sub chip die address + * @see RDCU-FRS-FN-1012 + * + * @returns 0 on success, otherwise error + */ + +int rdcu_edac_set_sub_chip_die_addr(uint32_t ca) +{ + if (ca > 0xFUL) + return -1; + + rdcu->sram_edac_ctrl &= ~(0xFUL << 12); + rdcu->sram_edac_ctrl |= (ca << 12); + + return 0; +} + + +/** + * @brief set EDAC control register read operation + * @see RDCU-FRS-FN-1012 + */ + +void rdcu_edac_set_ctrl_reg_read_op(void) +{ + rdcu->sram_edac_ctrl |= (0x1UL << 9); +} + + +/** + * @brief set EDAC control register write operation + * @see RDCU-FRS-FN-1012 + */ + +void rdcu_edac_set_ctrl_reg_write_op(void) +{ + rdcu->sram_edac_ctrl &= ~(0x1UL << 9); +} + + +/** + * @brief set EDAC to bypass + * @see RDCU-FRS-FN-1012 + */ + +void rdcu_edac_set_bypass(void) +{ + rdcu->sram_edac_ctrl |= (0x1UL << 8); +} + + +/** + * @brief set EDAC to normal operation + * @see RDCU-FRS-FN-1012 + */ + +void rdcu_edac_clear_bypass(void) +{ + rdcu->sram_edac_ctrl &= ~(0x1UL << 8); +} + + +/** + * @brief set EDAC SRAM scrubbing information + * @see RDCU-FRS-FN-1012 + */ + +void rdcu_edac_set_scrub_info(uint8_t nfo) +{ + rdcu->sram_edac_ctrl &= ~0xFFUL; + rdcu->sram_edac_ctrl |= (uint32_t) nfo & 0xFFUL; +} + + +/** + * @brief get EDAC sub chip die address + * @see RDCU-FRS-FN-1032 + */ + +uint32_t rdcu_edac_get_sub_chip_die_addr(void) +{ + return (rdcu->sram_edac_status >> 12) & 0xFUL; +} + + +/** + * @brief get EDAC bypass status + * @see RDCU-FRS-FN-1032 + * + * @returns 0: normal EDAC operation + * 1: EDAC function will be bypassed + */ + +uint32_t rdcu_edac_get_bypass_status(void) +{ + return (rdcu->sram_edac_status >> 8) & 0x1UL; +} + + +/** + * @brief get EDAC SRAM scrubbing information + * @see RDCU-FRS-FN-1032 + */ + +uint8_t rdcu_edac_get_scrub_info(void) +{ + return (uint8_t) (rdcu->sram_edac_ctrl & 0xFFUL); +} + + +/** + * @brief read data from the local SRAM mirror + * + * @param buf the buffer to read to (if NULL, the required size is returned) + * + * @param addr an address within the RDCU SRAM + * @param size the number of bytes read + * + * @returns the number of bytes read, < 0 on error + */ + +int rdcu_read_sram(void *buf, uint32_t addr, uint32_t size) +{ + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if (addr + size > RDCU_SRAM_END) + return -1; + + if (buf) + memcpy(buf, &rdcu->sram[addr], size); + + return (int)size; /* lol */ +} + + +/** + * @brief write arbitrary big-endian data to the local SRAM mirror + * + * @param buf the buffer to read from + * + * @param addr an address within the RDCU SRAM + * @param size the number of bytes read + * + * @returns the number of bytes written, < 0 on error + */ + +int rdcu_write_sram(void *buf, uint32_t addr, uint32_t size) +{ + if (!buf) + return 0; + + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if (addr + size > RDCU_SRAM_END) + return -1; + + if (buf) + memcpy(&rdcu->sram[addr], buf, size); + + return (int)size; /* lol */ +} + + +/** + * @brief write uint8_t formatted data to the local SRAM mirror. (This function + * is endian-safe.) + * + * @param buf the buffer to read from + * + * @param addr an address within the RDCU SRAM + * @param size the number of bytes read + * + * @returns the number of bytes written, < 0 on error + */ + +int rdcu_write_sram_8(uint8_t *buf, uint32_t addr, uint32_t size) +{ + return rdcu_write_sram(buf, addr, size); +} + + +/** + * @brief write uint16_t formatted data to the local SRAM mirror. This function + * is endian-safe. + * + * @param buf the buffer to read from + * + * @param addr an address within the RDCU SRAM + * @param size the number of bytes read + * + * @returns the number of bytes written, < 0 on error + */ + +int rdcu_write_sram_16(uint16_t *buf, uint32_t addr, uint32_t size) +{ + if (!buf) + return 0; + + if (size & 0x1) + return -1; + + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if (addr + size > RDCU_SRAM_END) + return -1; + +#if !(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return rdcu_write_sram(buf, addr, size); +#else + { + uint32_t i; + + for (i = 0; i < size/sizeof(uint16_t); i++){ + uint16_t *sram_buf = (uint16_t *)&rdcu->sram[addr]; + + sram_buf[i] = cpu_to_be16(buf[i]); + } + } + return (int)size; /* lol */ +#endif /* __BYTE_ORDER__ */ +} + + +/** + * @brief write uint32_t formatted data to the local SRAM mirror. This function + * is endian-safe. + * + * @param buf the buffer to read from + * + * @param addr an address within the RDCU SRAM + * @param size the number of bytes read + * + * @returns the number of bytes written, < 0 on error + */ + +int rdcu_write_sram_32(uint32_t *buf, uint32_t addr, uint32_t size) +{ + if (!buf) + return 0; + + if (size & 0x3) + return -1; + + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if (addr + size > RDCU_SRAM_END) + return -1; + +#if !(__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + return rdcu_write_sram(buf, addr, size); +#else + { + uint32_t i; + + for (i = 0; i < size/sizeof(uint32_t); i++){ + uint32_t *sram_buf = (uint32_t *)&rdcu->sram[addr]; + + sram_buf[i] = cpu_to_be32(buf[i]); + } + } + return (int)size; /* lol */ +#endif /* __BYTE_ORDER__ */ +} + + +/** + * @brief sync the FPGA version (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_fpga_version(void) +{ + return rdcu_sync(rdcu_read_cmd_fpga_version, &rdcu->fpga_version, 0); +} + + +/** + * @brief sync the RDCU status register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_rdcu_status(void) +{ + return rdcu_sync(rdcu_read_cmd_rdcu_status, &rdcu->rdcu_status, 0); +} + + +/** + * @brief sync the LVDS core status register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_lvds_core_status(void) +{ + return rdcu_sync(rdcu_read_cmd_lvds_core_status, + &rdcu->lvds_core_status, 0); +} + + +/** + * @brief sync the SpW link status register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_spw_link_status(void) +{ + return rdcu_sync(rdcu_read_cmd_spw_link_status, + &rdcu->spw_link_status, 0); +} + + +/** + * @brief sync the SpW Error Counter register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_spw_err_cntrs(void) +{ + return rdcu_sync(rdcu_read_cmd_spw_err_cntrs, + &rdcu->spw_err_cntrs, 0); +} + + +/** + * @brief sync the RMAP Last Error register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_rmap_last_err(void) +{ + return rdcu_sync(rdcu_read_cmd_rmap_last_err, + &rdcu->rmap_last_err, 0); +} + + +/** + * @brief sync the RMAP No-Reply Error Counter register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_rmap_no_reply_err_cntrs(void) +{ + return rdcu_sync(rdcu_read_cmd_rmap_no_reply_err_cntrs, + &rdcu->rmap_no_reply_err_cntrs, 0); +} + + +/** + * @brief sync the RMAP Packet Error Counter register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_rmap_pckt_err_cntrs(void) +{ + return rdcu_sync(rdcu_read_cmd_rmap_pckt_err_cntrs, + &rdcu->rmap_pckt_err_cntrs, 0); +} + + + +/** + * @brief sync an ADC values register (read only) + * + * @param id the ADC value register id (1-4) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_adc_values(int id) +{ + switch (id) { + case '1': + return rdcu_sync(rdcu_read_cmd_adc_values_1, + &rdcu->adc_values_1, 0); + case '2': + return rdcu_sync(rdcu_read_cmd_adc_values_2, + &rdcu->adc_values_2, 0); + case '3': + return rdcu_sync(rdcu_read_cmd_adc_values_3, + &rdcu->adc_values_3, 0); + case '4': + return rdcu_sync(rdcu_read_cmd_adc_values_4, + &rdcu->adc_values_4, 0); + default: + break; + } + + return -1; +} + + +/** + * @brief sync the ADC Status register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_adc_status(void) +{ + return rdcu_sync(rdcu_read_cmd_adc_status, + &rdcu->adc_status, 0); +} + + +/** + * @brief sync the Compressor Status register (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_status(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_status, + &rdcu->compr_status, 0); +} + + +/** + * @brief sync the RDCU Reset register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_rdcu_reset(void) +{ + return rdcu_sync(rdcu_write_cmd_rdcu_reset, + &rdcu->rdcu_reset, 4); +} + + +/** + * @brief sync the SpW Link Control register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_spw_link_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_spw_link_ctrl, + &rdcu->spw_link_ctrl, 4); +} + +/** + * @brief sync the LVDS Control register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_lvds_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_lvds_ctrl, + &rdcu->lvds_ctrl, 4); +} + + +/** + * @brief sync the Core Control register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_core_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_core_ctrl, + &rdcu->core_ctrl, 4); +} + + +/** + * @brief sync the ADC Control register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_adc_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_adc_ctrl, + &rdcu->adc_ctrl, 4); +} + + +/** + * @brief sync the Compressor Control register (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_compr_ctrl, + &rdcu->compr_ctrl, 4); +} + + +/** + * @brief sync the Compressor Parameter 1 (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compressor_param1(void) +{ + return rdcu_sync(rdcu_write_cmd_compressor_param1, + &rdcu->compressor_param1, 4); +} + + +/** + * @brief sync the Compressor Parameter 2 (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compressor_param2(void) +{ + return rdcu_sync(rdcu_write_cmd_compressor_param2, + &rdcu->compressor_param2, 4); +} + + +/** + * @brief sync the Adaptive Parameter 1 (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_adaptive_param1(void) +{ + return rdcu_sync(rdcu_write_cmd_adaptive_param1, + &rdcu->adaptive_param1, 4); +} + + +/** + * @brief sync the Adaptive Parameter 2 (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_adaptive_param2(void) +{ + return rdcu_sync(rdcu_write_cmd_adaptive_param2, + &rdcu->adaptive_param2, 4); +} + + +/** + * @brief sync the Data Start Address (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_data_start_addr(void) +{ + return rdcu_sync(rdcu_write_cmd_data_start_addr, + &rdcu->data_start_addr, 4); +} + + +/** + * @brief sync the Model Start Address (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_model_start_addr(void) +{ + return rdcu_sync(rdcu_write_cmd_model_start_addr, + &rdcu->model_start_addr, 4); +} + + +/** + * @brief sync the Number of Samples (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_num_samples(void) +{ + return rdcu_sync(rdcu_write_cmd_num_samples, + &rdcu->num_samples, 4); +} + + +/** + * @brief sync the Model Start Address (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_new_model_start_addr(void) +{ + return rdcu_sync(rdcu_write_cmd_new_model_start_addr, + &rdcu->new_model_start_addr, 4); +} + + +/** + * @brief sync the Compressed Data Buffer Start Address (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_buf_start_addr(void) +{ + return rdcu_sync(rdcu_write_cmd_compr_data_buf_start_addr, + &rdcu->compr_data_buf_start_addr, 4); +} + + +/** + * @brief sync the Compressed Data Buffer Length (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_buf_len(void) +{ + return rdcu_sync(rdcu_write_cmd_compr_data_buf_len, + &rdcu->compr_data_buf_len, 4); +} + + +/** + * @brief sync the Used Parameter 1 (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_used_param1(void) +{ + return rdcu_sync(rdcu_read_cmd_used_param1, &rdcu->used_param1, 0); +} + + +/** + * @brief sync the Used Parameter 2 (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_used_param2(void) +{ + return rdcu_sync(rdcu_read_cmd_used_param2, &rdcu->used_param2, 0); +} + + +/** + * @brief sync the Compressed Data Start Address (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_start_addr(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_data_start_addr, + &rdcu->compr_data_start_addr, 0); +} + + +/** + * @brief sync the Compressed Data Start Size (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_size(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_data_size, + &rdcu->compr_data_size, 0); +} + + +/** + * @brief sync the Compressed Data Adaptive 1 Size (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_adaptive_1_size(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_data_adaptive_1_size, + &rdcu->compr_data_adaptive_1_size, 0); +} + + +/** + * @brief sync the Compressed Data Adaptive 2 Size (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_data_adaptive_2_size(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_data_adaptive_2_size, + &rdcu->compr_data_adaptive_2_size, 0); +} + + +/** + * @brief sync the Compression Error (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_compr_error(void) +{ + return rdcu_sync(rdcu_read_cmd_compr_error, + &rdcu->compr_error, 0); +} + + +/** + * @brief sync the Model Info Start Address (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_new_model_addr_used(void) +{ + return rdcu_sync(rdcu_read_cmd_new_model_addr_used, + &rdcu->new_model_addr_used, 0); +} + + +/** + * @brief sync the Model Info Length (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_samples_used(void) +{ + return rdcu_sync(rdcu_read_cmd_samples_used, + &rdcu->samples_used, 0); +} + + +/** + * @brief sync the SRAM EDAC Control (write only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_sram_edac_ctrl(void) +{ + return rdcu_sync(rdcu_write_cmd_sram_edac_ctrl, + &rdcu->sram_edac_ctrl, 4); +} + + +/** + * @brief sync the SRAM EDAC Status (read only) + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_sram_edac_status(void) +{ + return rdcu_sync(rdcu_read_cmd_sram_edac_status, + &rdcu->sram_edac_status, 0); +} + + +/** + * @brief sync a range of 32 bit words of the local mirror to the remote SRAM + * + * @param addr and address within the remote SRAM + * @param size the number of bytes to sync + * @param mtu the maximum transport unit per RMAP packet; choose wisely + * + * @note due to restrictions, the number of bytes and mtu must be a multiple + * of 4; the address must be aligned to 32-bits as well + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_mirror_to_sram(uint32_t addr, uint32_t size, uint32_t mtu) +{ + int ret; + + uint32_t sent = 0; + uint32_t tx_bytes; + + + if (mtu & 0x3) + return -1; + + if (addr & 0x3) + return -1; + + if (size & 0x3) + return -1; + + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if ((addr + size) > (RDCU_SRAM_END + 1)) + return -1; + + + tx_bytes = size; + + while (tx_bytes >= mtu) { + + ret = rdcu_sync_data(rdcu_write_cmd_data, addr + sent, + &rdcu->sram[addr + sent], mtu, 0); + if (ret > 0) + continue; + + if (ret < 0) + return -1; + + + sent += mtu; + tx_bytes -= mtu; + } + + while (tx_bytes) { + ret = rdcu_sync_data(rdcu_write_cmd_data, addr + sent, + &rdcu->sram[addr + sent], tx_bytes, 0); + if (ret > 0) + continue; + + if (ret < 0) + return -1; + + tx_bytes = 0; + } + + + return 0; +} + + +/** + * @brief sync a range of 32 bit words of the remote SRAM to the local mirror + * + * @param addr an address within the remote SRAM + * @param size the number of bytes to sync + * @param mtu the maximum transport unit per RMAP packet; choose wisely + * + * @note due to restrictions, the number of bytes and mtu must be a multiple + * of 4; the address must be aligned to 32-bits as well + * + * @returns 0 on success, otherwise error + */ + +int rdcu_sync_sram_to_mirror(uint32_t addr, uint32_t size, uint32_t mtu) +{ + int ret; + + uint32_t recv = 0; + uint32_t rx_bytes; + + + if (mtu & 0x3) + return -1; + + if (addr & 0x3) + return -1; + + if (size & 0x3) + return -1; + + if (addr > RDCU_SRAM_END) + return -1; + + if (size > RDCU_SRAM_SIZE) + return -1; + + if ((addr + size) > (RDCU_SRAM_END + 1)) + return -1; + + + rx_bytes = size; + + while (rx_bytes >= mtu) { + + ret = rdcu_sync_data(rdcu_read_cmd_data, addr + recv, + &rdcu->sram[addr + recv], mtu, 1); + +#if 0 + while (rdcu_rmap_sync_status() > 3) + ; +#endif + + if (ret > 0) + continue; + + if (ret < 0) + return -1; + + recv += mtu; + rx_bytes -= mtu; + } + + while (rx_bytes) { + ret = rdcu_sync_data(rdcu_read_cmd_data, addr + recv, + &rdcu->sram[addr + recv], rx_bytes, 1); + if (ret > 0) + continue; + + if (ret < 0) + return -1; + + rx_bytes = 0; + } + + + return 0; +} + + +/** + * @brief sync a range of 32 bit words of the remote SRAM to the local mirror + * and form the local mirror to the remote SRAM in parallel + * + * @param rx_addr the read address within the remote SRAM + * @param rx_size the number of bytes to sync to the mirror + * @param tx_addr the write address within the remote SRAM + * @param tx_size the number of bytes to sync to the SRAM + * @param mtu the maximum transport unit per RMAP packet; choose wisely + * + * @note due to restrictions, the number of bytes and mtu must be a multiple + * of 4; the address must be aligned to 32-bits as well + * + * @returns 0 on succes, otherwise error + */ + +int rdcu_sync_sram_mirror_parallel(uint32_t rx_addr, uint32_t rx_size, + uint32_t tx_addr, uint32_t tx_size, + uint32_t mtu) +{ + int ret; + + uint32_t recv = 0; + uint32_t sent = 0; + uint32_t rx_bytes; + uint32_t tx_bytes; + + + if (mtu & 0x3) + return -1; + + if (rx_addr & 0x3) + return -1; + + if (tx_addr & 0x3) + return -1; + + if (rx_size & 0x3) + return -1; + + if (tx_size & 0x3) + return -1; + + if (rx_addr > RDCU_SRAM_END) + return -1; + + if (tx_addr > RDCU_SRAM_END) + return -1; + + if (rx_size > RDCU_SRAM_SIZE) + return -1; + + if (tx_size > RDCU_SRAM_SIZE) + return -1; + + if ((rx_addr + rx_size) > (RDCU_SRAM_END + 1)) + return -1; + + if ((tx_addr + tx_size) > (RDCU_SRAM_END + 1)) + return -1; + + /* check buffer overlap */ + if (rx_addr < tx_addr+tx_size && rx_addr+rx_size > tx_addr) + return -1; + + + rx_bytes = rx_size; + tx_bytes = tx_size; + + while (rx_bytes || tx_bytes) { + if (rx_bytes) { + if (rx_bytes >= mtu) { + ret = rdcu_sync_data(rdcu_read_cmd_data, + rx_addr + recv, + &rdcu->sram[rx_addr + recv], + mtu, 1); +#if 0 + while (rdcu_rmap_sync_status() > 3) + ; +#endif + + if (ret < 0) + return -1; + + if (ret == 0) { + recv += mtu; + rx_bytes -= mtu; + } + } else { + ret = rdcu_sync_data(rdcu_read_cmd_data, + rx_addr + recv, + &rdcu->sram[rx_addr + recv], + rx_bytes, 1); + if (ret < 0) + return -1; + + if (ret == 0) + rx_bytes = 0; + } + } + + if (tx_bytes) { + if (tx_bytes >= mtu) { + ret = rdcu_sync_data(rdcu_write_cmd_data, + tx_addr + sent, + &rdcu->sram[tx_addr + sent], + mtu, 0); + + if (ret < 0) + return -1; + + if (ret == 0) { + sent += mtu; + tx_bytes -= mtu; + } + } else { + ret = rdcu_sync_data(rdcu_write_cmd_data, + tx_addr + sent, + &rdcu->sram[tx_addr + sent], + tx_bytes, 0); + + if (ret < 0) + return -1; + + if (ret == 0) + tx_bytes = 0; + } + } + } + return 0; +} + + + +/** + * @brief initialise the rdcu control library + * + * @returns 0 on success, otherwise error + */ + +int rdcu_ctrl_init(void) +{ + rdcu = (struct rdcu_mirror *) malloc(sizeof(struct rdcu_mirror)); + if (!rdcu){ + printf("Error allocating memory for the RDCU mirror\n"); + return -1; + } + + memset(rdcu, 0, sizeof(struct rdcu_mirror)); + +#if (__sparc__) + rdcu->sram = (uint8_t *) 0x60000000; +#else /* assume PC */ + + rdcu->sram = (uint8_t *) malloc(RDCU_SRAM_SIZE); + if (!rdcu->sram) { + printf("Error allocating memory for the RDCU SRAM mirror\n"); + return -1; + } +#endif + + memset(rdcu->sram, 0, RDCU_SRAM_SIZE); + + return 0; +} diff --git a/lib/rdcu_pkt_to_file.c b/lib/rdcu_pkt_to_file.c new file mode 100644 index 0000000..24c213e --- /dev/null +++ b/lib/rdcu_pkt_to_file.c @@ -0,0 +1,472 @@ +/** + * @file rdcu_pkt_to_file.c + * @author Dominik Loidolt (dominik.loidolt@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 RDCU packets to file library + * + * This library provided a rmap_rx and rmap_tx function for the rdcu_rmap + * library to write generated packets into text files. + * + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <limits.h> +#include <errno.h> +#include <sys/stat.h> + +#include "../include/rdcu_pkt_to_file.h" +#include "../include/cmp_rdcu_extended.h" +#include "../include/rdcu_rmap.h" +#include "../include/rdcu_ctrl.h" +#include "../include/rdcu_cmd.h" + +/* Name of directory were the RMAP packages are stored */ +static char tc_folder_dir[MAX_TC_FOLDER_DIR_LEN] = "TC_FILES"; + + +/** + * @brief set the directory name were the RMAP packages are stored + * + * @param dir_name Name of directory were the RMAP packages are stored + */ + +void set_tc_folder_dir(const char *dir_name) +{ + if (!dir_name) + return; + + strncpy(tc_folder_dir, dir_name, sizeof(tc_folder_dir)); + /* Ensure null-termination. */ + tc_folder_dir[sizeof(tc_folder_dir) - 1] = '\0'; + return; +} + + +/** + * @brief return a file to write with the name format dir_name/XXX.tc, where XXX + * is n_tc in decimal representation + * + * @param dir_name name of directory were the RMAP packages are stored + * @param n_tc number of TC commands + * + * @return pointer to the new file stream + * @see https://developers.redhat.com/blog/2018/05/24/detecting-string-truncation-with-gcc-8/ + */ + +static FILE *open_file(const char *dir_name, int n_tc) +{ + char *pathname; + FILE *fp; + int n; + + errno = 0; + n = snprintf(NULL, 0, "%s/%04d.tc", dir_name, n_tc); + if (n < 0) { + perror("snprintf failed"); + abort(); + } + + errno = 0; + pathname = (char *)malloc((size_t)n + 1); + if (!pathname) { + perror("malloc failed"); + abort(); + } + + errno = 0; + n = snprintf(pathname, (size_t)n + 1, "%s/%04d.tc", dir_name, n_tc); + if (n < 0) { + perror("snprintf failed"); + abort(); + } + + fp = fopen(pathname, "w"); + free(pathname); + return fp; +} + + +/** + * @brief Implementation of the rmap_rx function for the rdcu_rmap lib. All + * generated packages are write to a file. + */ + +static int32_t rmap_tx_to_file(const void *hdr, uint32_t hdr_size, + const uint8_t non_crc_bytes, const void *data, + uint32_t data_size) +{ + static int n_pkt = 1; /* number of packets */ + static char tc_folder_dir_old[MAX_TC_FOLDER_DIR_LEN] = {0}; + uint8_t *blob = NULL; + int n, i; + FILE *fp; + + if (hdr == NULL) + return 0; + + /* make sure that string is null-terminated */ + tc_folder_dir[MAX_TC_FOLDER_DIR_LEN - 1] = '\0'; + + if (strcmp(tc_folder_dir, tc_folder_dir_old) != 0) { + struct stat st = { 0 }; + + n_pkt = 1; + + /* creating a directory if that directory does not exist */ + if (stat(tc_folder_dir, &st) == -1) { + int err; + #if defined(_WIN32) || defined(_WIN64) + err = mkdir(tc_folder_dir); + #else + err = mkdir(tc_folder_dir, 0700); + #endif + if (err) + return -1; + } + + strncpy(tc_folder_dir_old, tc_folder_dir, + sizeof(tc_folder_dir_old)); + tc_folder_dir_old[sizeof(tc_folder_dir_old) - 1] = '\0'; + } + + n = rdcu_package(NULL, hdr, hdr_size, non_crc_bytes, data, data_size); + blob = malloc(n); + if (!blob) { + printf("malloc for tx_pkt faild\n"); + return -1; + } + + n = rdcu_package(blob, hdr, hdr_size, non_crc_bytes, data, data_size); + + fp = open_file(tc_folder_dir, n_pkt); + + if (fp == NULL) { + perror("fopen()"); + free(blob); + return -1; + } + + for (i = 0; i < n; i++) { + /* printf("%02X ", blob[i]); */ + fprintf(fp, "%02X ", blob[i]); + /* if (i && !((i + 1) % 40)) */ + /* printf("\n"); */ + } + + /* printf("\n"); */ + fprintf(fp, "\n"); + fclose(fp); + + free(blob); + + n_pkt++; + return 0; +} + + +/** + * @brief Dummy implementation of the rmap_rx function for the rdcu_rmap lib. We + * do not want to receive any packages. + */ + +static uint32_t rmap_rx_dummy(uint8_t *pkt) +{ + (void)(pkt); + return 0; +} + + +/** + * @brief read out .rdcu_pkt_mode_cfg configure file + * + * @param icu_addr RMAP source logical address + * @param rdcu_addr RMAP destination logical address + * @param mtu the maximum data transfer size per unit + * + * @returns 0 on success, otherwise error + */ + +static int read_rdcu_pkt_mode_cfg(uint8_t *icu_addr, uint8_t *rdcu_addr, + int *mtu) +{ + /* TODO: Build string" %s/.rdcu_pkt_mode_cfg", RDCU_PKT_MODE_DIR */ + char line[256]; + char *end; + unsigned int read_all = 0; + FILE *fp = fopen(".rdcu_pkt_mode_cfg", "r"); + + *icu_addr = 0; + *rdcu_addr = 0; + *mtu = 0; + + if (fp == NULL) { + perror("fopen()"); + return -1; + } + + while (fgets(line, sizeof(line), fp)) { + size_t l; + long i; + char *p; + + p = strchr(line, '\n'); + if (p) { + *p = '\0'; + } else { + fprintf(stderr, "Error read in line to long.\n"); + return -1; + } + + if (line[0] == ' ' || line[0] == '\t' || line[0] == '#') + continue; + if (!strncmp(line, "ICU_ADDR", l = strlen("ICU_ADDR"))) { + end = NULL; + errno = 0; + i = strtol(line + l, &end, 0); + if (end == line + l || errno == ERANGE || i < 0 || + i > 0xFF) { + fprintf(stderr, "Error reading ICU_ADDR.\n"); + errno = 0; + return -1; + } + *icu_addr = (uint8_t)i; + read_all |= 1UL << 0; + continue; + } + if (!strncmp(line, "RDCU_ADDR", l = strlen("RDCU_ADDR"))) { + end = NULL; + errno = 0; + i = strtol(line + l, &end, 0); + if (end == line + l || errno == ERANGE || i < 0 + || i > 0xFF) { + fprintf(stderr, "Error reading RDCU_ADDR.\n"); + errno = 0; + return -1; + } + *rdcu_addr = (uint8_t)i; + read_all |= 1UL << 1; + continue; + } + if (!strncmp(line, "MTU", l = strlen("MTU"))) { + end = NULL; + errno = 0; + i = strtol(line + l, &end, 0); + if (end == line + l || errno == ERANGE || i < 0 || + i > INT_MAX) { + fprintf(stderr, "Error reading MTU.\n"); + errno = 0; + return -1; + } + *mtu = (int)i; + read_all |= 1UL << 2; + continue; + } + } + fclose(fp); + + /* all keywords read? */ + if (read_all < 0x7) + return -1; + + printf("Use ICU_ADDR = %#02X, RDCU_ADDR = %#02X and MTU = %d for the " + "RAMP packets.\n", *icu_addr, *rdcu_addr, *mtu); + + return 0; +} + + +/** + * @brief initialise the RDCU packets to file library + * + * @note use the .rdcu_pkt_mode_cfg file to read the icu_addr, rdcu_addr and mtu + * parameters + * + * @returns 0 on success, otherwise error + */ + +int init_rmap_pkt_to_file(void) +{ + uint8_t icu_addr, rdcu_addr; + int mtu; + + if (read_rdcu_pkt_mode_cfg(&icu_addr, &rdcu_addr, &mtu)) + return -1; + rdcu_ctrl_init(); + rdcu_set_source_logical_address(icu_addr); + rdcu_set_destination_logical_address(rdcu_addr); + rdcu_set_destination_key(RDCU_DEST_KEY); + rdcu_rmap_init(mtu, rmap_tx_to_file, rmap_rx_dummy); + return 0; +} + + +/** + * @brief generate the rmap packets to set up a RDCU compression + * @note note that the initialization function init_rmap_pkt_to_file() must be + * executed before + * @note the configuration of the ICU_ADDR, RDCU_ADDR, MTU settings are in the + * .rdcu_pkt_mode_cfg file + * + * @param cfg compressor configuration contains all parameters required for + * compression + * + * @returns 0 on success, error otherwise + */ + +int gen_write_rdcu_pkts(const struct cmp_cfg *cfg) +{ + struct stat st = { 0 }; + + if (!cfg) + return -1; + + /* creating TC_DIR directory if that directory does not exist */ + if (stat(TC_DIR, &st) == -1) { + int err; + #if defined(_WIN32) || defined(_WIN64) + err = mkdir(TC_DIR); + #else + err = mkdir(TC_DIR, 0700); + #endif + if (err) + return -1; + } + + set_tc_folder_dir(TC_DIR "/compress_data"); + if (rdcu_compress_data(cfg)) + return -1; + + return 0; +} + + +/** + * @brief generate the rmap packets to read the result of a RDCU compression + * @note note that the initialization function init_rmap_pkt_to_file() must be + * executed before + * @note the configuration of the ICU_ADDR, RDCU_ADDR, MTU settings are in the + * .rdcu_pkt_mode_cfg file + * + * @param info compressor information contains information of an executed + * compression + * + * @returns 0 on success, error otherwise + */ + +int gen_read_rdcu_pkts(const struct cmp_info *info) +{ + int s; + void *tmp_buf; + struct stat st = { 0 }; + + if (!info) + return -1; + + /* creating TC_DIR directory if that directory does not exist */ + if (stat(TC_DIR, &st) == -1) { + int err; + #if defined(_WIN32) || defined(_WIN64) + err = mkdir(TC_DIR); + #else + err = mkdir(TC_DIR, 0700); + #endif + if (err) + return -1; + } + + set_tc_folder_dir(TC_DIR "/read_status"); + if (rdcu_read_cmp_status(NULL)) + return -1; + + set_tc_folder_dir(TC_DIR "/read_info"); + if (rdcu_read_cmp_info(NULL)) + return -1; + + set_tc_folder_dir(TC_DIR "/read_cmp_data"); + s = rdcu_read_cmp_bitstream(info, NULL); + if (s < 0) + return -1; + tmp_buf = malloc((size_t)s); + if (!tmp_buf) + return -1; + s = rdcu_read_cmp_bitstream(info, tmp_buf); + free(tmp_buf); + if (s < 0) + return -1; + + if (model_mode_is_used(info->cmp_mode_used)) { + set_tc_folder_dir(TC_DIR "/read_upmodel"); + s = rdcu_read_model(info, NULL); + if (s < 0) + return -1; + tmp_buf = malloc((size_t)s); + if (!tmp_buf) + return -1; + s = rdcu_read_model(info, tmp_buf); + free(tmp_buf); + if (s < 0) + return -1; + } + return 0; +} + + +/** + * @brief generate the rmap packets to set up a RDCU compression, read the + * bitstream and the updated model in parallel to write the data to compressed + * and the model and start the compression + * @note the compressed data are read from cfg->rdcu_buffer_adr with the length + * of last_cmp_size + * @note note that the initialization function init_rmap_pkt_to_file() must be + * executed before + * @note the configuration of the ICU_ADDR, RDCU_ADDR, MTU settings are in the + * .rdcu_pkt_mode_cfg file + * + * @param cfg compressor configuration contains all parameters required for + * compression + * @param last_info compression information from the last executed + * compression + * + * @returns 0 on success, error otherwise + */ + +int gen_rdcu_parallel_pkts(const struct cmp_cfg *cfg, + const struct cmp_info *last_info) +{ + struct stat st = { 0 }; + + if (!cfg) + return -1; + + /* creating TC_DIR directory if that directory does not exist */ + if (stat(TC_DIR, &st) == -1) { + int err; + #if defined(_WIN32) || defined(_WIN64) + err = mkdir(TC_DIR); + #else + err = mkdir(TC_DIR, 0700); + #endif + if (err) + return -1; + } + + set_tc_folder_dir(TC_DIR "/compress_data_parallel"); + if (rdcu_compress_data_parallel(cfg, last_info)) + return -1; + + return 0; +} diff --git a/lib/rdcu_rmap.c b/lib/rdcu_rmap.c new file mode 100644 index 0000000..e25241c --- /dev/null +++ b/lib/rdcu_rmap.c @@ -0,0 +1,822 @@ +/** + * @file rdcu_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 RDCU link interface + * @see FPGA Requirement Specification PLATO-IWF-PL-RS-005 Issue 0.7 + * + * + * 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 + * RDCU'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 rdcu_ctrl_sync_status() + * + * This is probably the nicest solution when it comes to call overhead, but it + * requires 8 MiB of memory for the SRAM mirror and the some for the registers. + * + * 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: in order to run this on the GR712RC eval board, we set the SRAM mirror + * image to the boards SDRAM in rdcu_ctrl_init() and just malloc() it for + * the PC (see rdcu_ctrl_init) + * + * The interface requires that you provide an RX and a TX function, + * see rdcu_ctrl_init for the call interface. + * The TX function shall to return 0 on success, everything else + * is considered an error in submission. The RX function shall return + * the size of the packet buffer and accept NULL as call argument, on + * which it shall return the buffer size required to store the next + * pending packet. + * You can use these functions to adapt the actual backend, i.e. use + * your particular SpW interface or just redirect RX/TX to files + * or via a network connection. + * + * NOTE: We don't have to serve more than one RDCU 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 <byteorder.h> +#include <rmap.h> +#include <rdcu_rmap.h> + + + +static uint8_t rdcu_addr; +static uint8_t icu_addr; + +static uint8_t *dpath; /* destination path (to the RDCU) */ +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 size_t 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 /1* GRSPW2 TX descriptor limit *1/ */ +#define TRANS_LOG_SIZE 64000 /* increased because there are no response packets during packet generation */ +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 rdcu_process_rx(void) +{ + int n; + int cnt = 0; + + uint32_t *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, n); + 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 & 0x3) { + printf("Error: response packet data size is not a " + "multiple of 4, transaction dropped\n"); + + trans_log_release_slot(rp->tr_id); + rmap_erase_packet(rp); + return -1; + } + + + if (rp->data_len) { + + uint8_t crc8; + + /* convert endianess if needed */ +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + { + unsigned int i; + uint32_t *p = (uint32_t *) rp->data; + + for (i = 0; i < (rp->data_len / 4); i++) + be32_to_cpus(&p[i]); + } +#endif /* __BYTE_ORDER__ */ + + + crc8 = rmap_crc8(rp->data, rp->data_len); + + if (crc8 != rp->data_crc) { + + printf("Error: data CRC8 mismatch, data invalid or " + "packet truncated. Transaction dropped\n"); + + trans_log_release_slot(rp->tr_id); + rmap_erase_packet(rp); + return -1; + } + + 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 rdcu_submit_tx(const uint8_t *cmd, int cmd_size, + const uint8_t *data, int data_size) +{ + /* try to process pending responses */ + rdcu_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!\n"); + 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 rdcu_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, rdcu_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; + } + + memset(cmd, 0, n); + + n = rmap_build_hdr(pkt, cmd); + + rmap_erase_packet(pkt); + + return n; +} + + + + + +/** + * @brief submit a sync command + * + * @param fn the RDCU 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) + * + * @note data_len must be a multiple of 4 + * @note all data is treated (and byte swapped) as 32 bit words + * + * @return 0 on success, otherwise error + */ + + +int rdcu_sync(int (*fn)(uint16_t trans_id, uint8_t *cmd), + void *addr, int data_len) +{ + int n; + int slot; + + uint8_t *rmap_cmd; + + + if (data_len & 0x3) + return -1; + + 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; + } + + /* convert endianess if needed */ +#if (__BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__) + if (data_len) + { + int i; + uint32_t *tmp_buf = alloca(data_len); + uint32_t *p = (uint32_t *) addr; + + for (i = 0; i < (data_len / 4); i++) + tmp_buf[i] = cpu_to_be32(p[i]); + + addr = tmp_buf; + } +#endif /* __BYTE_ORDER__ */ + + n = rdcu_submit_tx(rmap_cmd, n, addr, data_len); + free(rmap_cmd); + + return n; +} + + + +/** + * @brief submit a data sync command + * + * @param fn a RDCU 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 rdcu_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; + + + + rdcu_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 = rdcu_submit_tx(rmap_cmd, n, NULL, 0); + else + n = rdcu_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) + * + * @note data_size must be a multiple of 4 + * @note this function will convert all data to big endian as 32 bit words + * + * @returns the size of the blob or 0 on error + */ + +int rdcu_package(uint8_t *blob, + const uint8_t *cmd, uint32_t cmd_size, + const uint8_t non_crc_bytes, + const uint8_t *data, uint32_t data_size) +{ + int n; + int has_data_crc = 0; + struct rmap_instruction *ri; + + + if (data_size & 0x3) /* must be multiple of 4 */ + return -1; + + 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: + case RMAP_WRITE_ADDR_SINGLE_VERIFY_REPLY: + case RMAP_WRITE_ADDR_INC_VERIFY_REPLY: + case RMAP_WRITE_ADDR_INC_REPLY: + 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(&blob[cmd_size + 1], 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 RDCU + * @param addr the address + */ + +void rdcu_set_destination_logical_address(uint8_t addr) +{ + rdcu_addr = addr; +} + +/** + * @brief sets the logical address of the ICU + * @param addr the address + */ + +void rdcu_set_source_logical_address(uint8_t addr) +{ + icu_addr = addr; +} + + +/** + * @brief set the destination path to the RDCU + * @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 rdcu_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 rdcu_set_return_path(uint8_t *path, uint8_t len) +{ + if (len > RMAP_MAX_REPLY_PATH_LEN) + return -1; + + if (len & 0x3) + return -1; /* not a multiple of 4 */ + + 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 rdcu_set_destination_key(uint8_t key) +{ + dst_key = key; +} + + +/** + * @brief get the configured data MTU + * + * @returns the mtu + */ + +size_t rdcu_get_data_mtu(void) +{ + return data_mtu; +} + + +/** + * @brief get the RDCU <-> ICU mirror RMAP synchronisation status + * + * @returns 0: synchronised, > 0: operations pending + */ + +int rdcu_rmap_sync_status(void) +{ + /* try to process pending responses */ + rdcu_process_rx(); + + return trans_log.pending; +} + + +/** + * @brief reset all entries in the RMAP transaction log + */ + +void rdcu_rmap_reset_log(void) +{ + memset(trans_log.in_use, 0, TRANS_LOG_SIZE); + trans_log.pending = 0; +} + + +/** + * @brief initialise the rdcu 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 rdcu_rmap_init(size_t 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/lib/rmap.c b/lib/rmap.c new file mode 100644 index 0000000..908b622 --- /dev/null +++ b/lib/rmap.c @@ -0,0 +1,778 @@ +/** + * @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 + * + * @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) + * + * @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>, ... + * @param len the data length of the buffer (in bytes) + * + * @returns an rmap packet, containing the decoded buffer including any data, + * NULL on error + */ + +struct rmap_pkt *rmap_pkt_from_buffer(uint8_t *buf, uint32_t len) +{ + size_t n = 0; + size_t i; + int min_hdr_size; + + struct rmap_pkt *pkt = NULL; + + + if (!buf) + goto error; + + if (len < RMAP_HDR_MIN_SIZE_WRITE_REP) { + printf("buffer len is smaller than the smallest RMAP packet\n"); + 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]; + + min_hdr_size = rmap_get_min_hdr_size(pkt); + if (min_hdr_size < 0) + goto error; + + if (len < (uint32_t)min_hdr_size) { + printf("buffer len is smaller than the contained RMAP packet\n"); + goto error; + } + + + if (pkt->ri.cmd_resp) { + pkt->rpath_len = pkt->ri.reply_addr_len << 2; + if (len < (uint32_t)min_hdr_size + pkt->rpath_len) { + printf("buffer is smaller than the contained RMAP packet\n"); + goto error; + } + + 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]; + } + + pkt->hdr_crc = buf[RMAP_HEADER_CRC]; + + if (pkt->data_len) { + if (len < RMAP_DATA_START + n + pkt->data_len + 1) { /* +1 for data CRC */ + printf("buffer len is smaller than the contained RMAP packet; buf len: %u bytes vs RMAP: %lu bytes needed\n", + len , RMAP_DATA_START + n + pkt->data_len); + goto error; + } + if (len > RMAP_DATA_START + n + pkt->data_len + 1) /* +1 for data CRC */ + printf("warning: the buffer is larger than the included RMAP packet\n"); + + 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]; + + /* final byte is data crc */ + pkt->data_crc = 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; + +#if (__sparc__) + printf("\tData length is %lu bytes:\n\t", len); +#else + printf("\tData length is %u bytes:\n\t", len); +#endif + + + 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/lib/tool_lib.c b/lib/tool_lib.c new file mode 100644 index 0000000..5318366 --- /dev/null +++ b/lib/tool_lib.c @@ -0,0 +1,1206 @@ +/** + * @file tool_lib.c + * @author Johannes Seelig (johannes.seelig@univie.ac.at) + * @author Dominik Loidolt (dominik.loidolt@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. + */ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <ctype.h> +#include <sys/stat.h> + +#include "../include/tool_lib.h" +#include "../include/cmp_support.h" +#include "../include/rdcu_cmd.h" +#include "../include/byteorder.h" + + +/** + * @brief print help information + * + * @param argv argument vector + */ + +void Print_Help(const char *argv) +{ + printf("usage: %s [options] [<argument>]\n", &argv[0]); + printf("General Options:\n"); + printf(" -h, --help Print this help text and exit\n"); + printf(" -V, --version Print program version and exit\n"); + printf(" -v, --verbose Print various debugging information\n"); + printf(" -n, --model_cfg Print a default model configuration and exit\n"); + printf(" --diff_cfg Print a default 1d-differencing configuration and exit\n"); + printf(" -a, --rdcu_par Print additional RDCU control parameters\n"); + printf(" -o <prefix> Use the <prefix> for output files\n"); + printf("Compression Options:\n"); + printf(" -c <file> File containing the compressing configuration\n"); + printf(" -d <file> File containing the data to be compressed\n"); + printf(" -m <file> File containing the model of the data to be compressed\n"); + printf(" --rdcu_pkt Generate RMAP packets for a RDCU compression\n"); + printf("Decompression Options:\n"); + printf(" -i <file> File containing the decompression information\n"); + printf(" -d <file> File containing the compressed data\n"); + printf(" -m <file> File containing the model of the compressed data\n"); + printf("\n"); +} + + +/** + * @brief opens a file with a name that is a concatenation of directory and file + * name. + * + * @param dirname first sting of concatenation + * @param filename security sting of concatenation + * + * @return a pointer to the opened file + * + * @see https://developers.redhat.com/blog/2018/05/24/detecting-string-truncation-with-gcc-8/ + */ + +static FILE *open_file(const char *dirname, const char *filename) +{ + FILE *fp; + char *pathname; + int n; + + if (!dirname) + return NULL; + + if(!filename) + return NULL; + + errno = 0; + n = snprintf(0, 0, "%s%s", dirname, filename); + + if (n < 0) { + perror("snprintf failed"); + abort(); + } + + errno = 0; + pathname = (char *) malloc((size_t)n + 1); + if (!pathname) { + perror("malloc failed"); + abort(); + } + + errno = 0; + n = snprintf(pathname, (size_t)n + 1, "%s%s", dirname, filename); + if (n < 0) { + perror("snprintf failed"); + abort(); + } + + fp = fopen(pathname, "w"); + free(pathname); + return fp; +} + + +/** + * @brief write a uint16_t buffer to a output file + * + * @param buf the buffer to write a file + * @param buf_len length of the buffer + * + * @param output_prefix file name without file extension + * @param name_extension file extension (with leading point character) + * + * @param verbose print verbose output if not zero + * + * @returns 0 on success, error otherwise + */ + +int write_to_file16(const uint16_t *buf, uint32_t buf_len, const char + *output_prefix, const char *name_extension, int verbose) +{ + uint32_t i; + FILE *fp; + + if (!buf) + abort(); + + if (buf_len == 0) + return 0; + + fp = open_file(output_prefix, name_extension); + if (fp == NULL) { + fprintf(stderr, "%s: %s%s: %s\n", PROGRAM_NAME, output_prefix, + name_extension, strerror(errno)); + return -1; + } + + for (i = 0; i < buf_len; i++) { + fprintf(fp, "%02X %02X",buf[i] >> 8 ,buf[i] & 0xFF); + if ((i + 1) % 16 == 0) + fprintf(fp, "\n"); + else + fprintf(fp, " "); + } + fprintf(fp, "\n"); + + fclose(fp); + + if (verbose) { + printf("\n\n"); + for (i = 0; i < buf_len; i++) { + printf("%02X %02X",buf[i] >> 8 ,buf[i] & 0xFF); + if ((i + 1) % 16 == 0) + printf("\n"); + else + printf(" "); + } + + printf("\n\n"); + } + + return 0; +} + + +/** + * @brief write a buffer to a output file + * + * @param buf the buffer to write a file + * @param buf_size size of the buffer in bytes + * + * @param output_prefix file name without file extension + * @param name_extension file extension (with leading point character) + * + * @param verbose print verbose output if not zero + * + * @returns 0 on success, error otherwise + */ + +int write_cmp_data_file(const void *buf, uint32_t buf_size, const char + *output_prefix, const char *name_extension, int verbose) +{ + unsigned int i; + FILE *fp; + uint8_t *p = (uint8_t *)buf; + + if (!buf) + abort(); + + if (buf_size == 0) + return 0; + + fp = open_file(output_prefix, name_extension); + if (fp == NULL) { + fprintf(stderr, "%s: %s%s: %s\n", PROGRAM_NAME, output_prefix, + name_extension, strerror(errno)); + return -1; + } + + for (i = 0; i < buf_size; i++) { + fprintf(fp, "%02X", p[i]); + if ((i + 1) % 32 == 0) + fprintf(fp, "\n"); + else + fprintf(fp, " "); + } + fprintf(fp, "\n"); + + fclose(fp); + + if (verbose) { + printf("\n\n"); + for (i = 0; i < buf_size; i++) { + printf("%02X", p[i]); + if ((i + 1) % 32 == 0) + printf("\n"); + else + printf(" "); + } + printf("\n\n"); + } + return 0; +} + + +/** + * @brief remove white spaces and tabs + * + * @param s input string + */ + +static void remove_spaces(char *s) +{ + const char *d; + + if (!s) + return; + + d = s; + + do { + while (*d == ' ' || *d == '\t') + ++d; + } while ((*s++ = *d++) != '\0'); +} + + +/** + * @brief remove comments (starting with # or /) and \n + * + * @param s input string + */ + +static void remove_comments(char *s) +{ + if (!s) + return; + + while (*s) { + if (*s == '#' || *s == '/' || *s == '\n') { + *s = '\0'; + break; + } + s++; + } + +} + + +/** + * @brief convert RDCU SRAM Address sting to integer + * + * @param addr string of the address + * + * @returns the value of the address, < 0 on error + */ + +static int sram_addr_to_int(const char *addr) +{ + unsigned long i; + char *end = NULL; + + if (addr == NULL) + return -1; + + i = strtoul(addr, &end, 0); + + if (end == addr || errno == ERANGE) { + printf("range error, got\n"); + errno = 0; + return -1; + } + + if (i > RDCU_SRAM_END) { + printf("%s: The sram address is out of the rdcu range\n", + PROGRAM_NAME); + return -1; + } + + if (i & 0x3) { + printf("The SRAM address is not 32 bit aligned\n"); + return -1; + } + + return (int) i; +} + + +/** + * @brief Interprets an uint32_t integer value in a byte string + * + * @param dep_str description sting of the read in value + * @param val_str value sting contain the value to convert in uint32_t + * @param red_val address for storing the converted result + * + * @returns 0 on success, error otherwise + * + * @see https://eternallyconfuzzled.com/atoi-is-evil-c-learn-why-atoi-is-awful + */ + +static int atoui32(const char *dep_str , const char *val_str, uint32_t *red_val) +{ + long temp; + char *end = NULL; + + if (!dep_str) + return -1; + + if (!val_str) + return -1; + + if (!red_val) + return -1; + + temp = strtol(val_str, &end, 10); + if (end != val_str && errno != ERANGE && temp >= 0 && temp <= + UINT32_MAX) { + *red_val = (uint32_t)temp; + return 0; + } else { + fprintf(stderr, "%s: Error read in %s.\n", PROGRAM_NAME, + dep_str); + *red_val = 0; + return -1; + } +} + + +/** +* @brief parse a file containing a compressing configuration +* +* @param fp FILE pointer +* @param cfg compression configuration structure holding the read in +* configuration +* +* @returns 0 on success, error otherwise +*/ + +static int parse_cfg(FILE *fp, struct cmp_cfg *cfg) +{ + char *token1, *token2; + char line[MAX_CONFIG_LINE]; + + if (!fp) + return -1; + + if (!cfg) + return -1; + + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') + continue; /* skip #'ed or empty lines */ + + if (!strchr(line, '\n')){ /* detect a to long line */ + fprintf(stderr, "%s: Error read in line to long. Maximal line length is %d characters.\n", + PROGRAM_NAME, MAX_CONFIG_LINE-1); + return -1; + } + + remove_comments(line); + remove_spaces(line); + + token1 = strtok(line, "="); + token2 = strtok(NULL, "="); + if (token1 == NULL) + continue; + if (token2 == NULL) + continue; + + + if (!strcmp(token1, "cmp_mode")) { + if (isalpha(*token2)) { /* check if mode is given as text or val*/ + if (!strcmp(token2, "MODE_RAW")) { + cfg->cmp_mode = 0; + continue; + } + if (!strcmp(token2, "MODE_MODEL_ZERO")) { + cfg->cmp_mode = 1; + continue; + } + if (!strcmp(token2, "MODE_DIFF_ZERO")) { + cfg->cmp_mode = 2; + continue; + } + if (!strcmp(token2, "MODE_MODEL_MULTI")) { + cfg->cmp_mode = 3; + continue; + } + if (!strcmp(token2, "MODE_DIFF_MULTI")) { + cfg->cmp_mode = 4; + continue; + } + fprintf(stderr, "%s: Error read in cmp_mode.\n", + PROGRAM_NAME); + } else { + if (atoui32(token1, token2, &cfg->cmp_mode)) + return -1; + continue; + } + } + if (!strcmp(token1, "golomb_par")) { + if (atoui32(token1, token2, &cfg->golomb_par)) + return -1; + continue; + } + if (!strcmp(token1, "spill")) { + if (atoui32(token1, token2, &cfg->spill)) + return -1; + continue; + } + if (!strcmp(token1, "model_value")) { + if (atoui32(token1, token2, &cfg->model_value)) + return -1; + continue; + } + if (!strcmp(token1, "round")) { + if (atoui32(token1, token2, &cfg->round)) + return -1; + continue; + } + if (!strcmp(token1, "ap1_golomb_par")) { + if (atoui32(token1, token2, &cfg->ap1_golomb_par)) + return -1; + continue; + } + if (!strcmp(token1, "ap1_spill")) { + if (atoui32(token1, token2, &cfg->ap1_spill)) + return -1; + continue; + } + if (!strcmp(token1, "ap2_golomb_par")) { + if (atoui32(token1, token2, &cfg->ap2_golomb_par)) + return -1; + continue; + } + if (!strcmp(token1, "ap2_spill")) { + if (atoui32(token1, token2, &cfg->ap2_spill)) + return -1; + continue; + } + if (!strcmp(token1, "rdcu_data_adr")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + printf("%s: Error read in rdcu_data_adr_par\n", + PROGRAM_NAME); + return -1; + } + cfg->rdcu_data_adr = (uint32_t)i; + continue; + } + if (!strcmp(token1, "rdcu_model_adr")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + printf("%s: Error read in rdcu_model_adr_par\n", + PROGRAM_NAME); + return -1; + } + cfg->rdcu_model_adr = (uint32_t)i; + continue; + } + if (!strcmp(token1, "rdcu_new_model_adr")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + printf("%s: Error read in rdcu_new_model_adr_par\n", + PROGRAM_NAME); + return -1; + } + cfg->rdcu_new_model_adr = (uint32_t)i; + continue; + } + if (!strcmp(token1, "samples")) { + if (atoui32(token1, token2, &cfg->samples)) + return -1; + continue; + } + if (!strcmp(token1, "rdcu_buffer_adr")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + printf("%s: Error read in rdcu_buffer_adr_par\n", + PROGRAM_NAME); + return -1; + } + cfg->rdcu_buffer_adr = (uint32_t)i; + continue; + } + if (!strcmp(token1, "buffer_length")) { + if (atoui32(token1, token2, &cfg->buffer_length)) + return -1; + continue; + } + } + return 0; +} + + +/** + * @brief reading a compressor configuration file + * + * @param file_name file containing the compression configuration file + * @param cfg compression configuration structure holding the read in + * configuration + * @param verbose_en print verbose output if not zero + * + * @returns 0 on success, error otherwise + */ + +int read_cmp_cfg(const char *file_name, struct cmp_cfg *cfg, int verbose_en) +{ + FILE *fp; + + if (!file_name) + abort(); + + if (!cfg) + abort(); + + if (strstr(file_name, ".info")) { + fprintf(stderr, "%s: %s: .info file extension found on configuration file. You may have selected the wrong file.\n", + PROGRAM_NAME, file_name); + } + + fp = fopen(file_name, "r"); + + if (fp == NULL) { + fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, file_name, + strerror(errno)); + return -1; + } + + if (parse_cfg(fp, cfg)) + return -1; + + fclose(fp); + + if (verbose_en) { + printf("\n\n"); + print_cmp_cfg(cfg); + printf("\n"); + } + + if (cfg->buffer_length < cfg->samples/2) { + fprintf(stderr, + "%s: warning: buffer_length: %u is relative small to the chosen samples parameter of %u.\n", + PROGRAM_NAME, cfg->buffer_length, cfg->samples); + } + + return 0; +} + + +/** + * @brief parse a file containing a decompression information + * + * @param fp FILE pointer + * @param info decompression information structure holding the read in + * information + * + * @returns 0 on success, error otherwise + */ + +static int parse_info(FILE *fp, struct cmp_info *info) +{ + char *token1, *token2; + char line[MAX_CONFIG_LINE]; + + if (!fp) + return -1; + + if (!info) + return -1; + + while (fgets(line, sizeof(line), fp) != NULL) { + if (line[0] == '#' || line[0] == '\n') + continue; /* skip #'ed or empty lines */ + + if (!strchr(line, '\n')){ /* detect a to long line */ + fprintf(stderr, "%s: Error read in line to long. Maximal line length is %d characters.\n", + PROGRAM_NAME, MAX_CONFIG_LINE-1); + return -1; + } + + remove_comments(line); + remove_spaces(line); + + /* Token will point to the part before the search-var. */ + token1 = strtok(line, "="); + token2 = strtok(NULL, "="); + if (token1 == NULL) + continue; + if (token2 == NULL) + continue; + + + if (!strcmp(token1, "cmp_mode_used")) { + if (isalpha(*token2)) { /* check if mode is given as text or val*/ + if (!strcmp(token2, "MODE_RAW")) { + info->cmp_mode_used = 0; + continue; + } + if (!strcmp(token2, "MODE_MODEL_ZERO")) { + info->cmp_mode_used = 1; + continue; + } + if (!strcmp(token2, "MODE_DIFF_ZERO")) { + info->cmp_mode_used = 2; + continue; + } + if (!strcmp(token2, "MODE_MODEL_MULTI")) { + info->cmp_mode_used = 3; + continue; + } + if (!strcmp(token2, "MODE_DIFF_MULTI")) { + info->cmp_mode_used = 4; + continue; + } + fprintf(stderr, "%s: Error read in cmp_mode_used.\n", + PROGRAM_NAME); + return -1; + } else { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + info->cmp_mode_used = tmp; + } + continue; + } + if (!strcmp(token1, "model_value_used")) { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + if (tmp <= UINT8_MAX) { + info->model_value_used = (uint8_t)tmp; + } else { + fprintf(stderr, "%s: Error read in %s.\n", + PROGRAM_NAME, token1); + return -1; + } + continue; + } + if (!strcmp(token1, "round_used")) { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + if (tmp <= 0xF) { /* max value of 4 bits */ + info->round_used = (uint8_t)tmp; + } else { + fprintf(stderr, "%s: Error read in %s.\n", + PROGRAM_NAME, token1); + return -1; + } + continue; + } + if (!strcmp(token1, "spill_used")) { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + info->spill_used = (unsigned char)tmp; + continue; + } + if (!strcmp(token1, "golomb_par_used")) { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + info->golomb_par_used = tmp; + continue; + } + if (!strcmp(token1, "samples_used")) { + if (atoui32(token1, token2, &info->samples_used)) + return -1; + continue; + } + if (!strcmp(token1, "cmp_size")) { + if (atoui32(token1, token2, &info->cmp_size)) + return -1; + continue; + } + + if (!strcmp(token1, "ap1_cmp_size")) { + if (atoui32(token1, token2, &info->ap1_cmp_size)) + return -1; + continue; + } + if (!strcmp(token1, "ap2_cmp_size")) { + if (atoui32(token1, token2, &info->ap2_cmp_size)) + return -1; + continue; + } + + if (!strcmp(token1, "rdcu_new_model_adr_used")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + fprintf(stderr, "%s: Error read in rdcu_new_model_adr_used\n", + PROGRAM_NAME); + return -1; + } + info->rdcu_new_model_adr_used = (uint32_t)i; + continue; + } + if (!strcmp(token1, "rdcu_cmp_adr_used")) { + int i = sram_addr_to_int(token2); + + if (i < 0) { + fprintf(stderr,"%s: Error read in " + "rdcu_cmp_adr_used\n", + PROGRAM_NAME); + return -1; + } + info->rdcu_cmp_adr_used = (uint32_t)i; + continue; + } + if (!strcmp(token1, "cmp_err")) { + uint32_t tmp; + + if (atoui32(token1, token2, &tmp)) + return -1; + if (tmp <= UINT16_MAX) { + info->cmp_err = (uint16_t)tmp; + } else { + fprintf(stderr, "%s: Error read in %s.\n", + PROGRAM_NAME, token1); + return -1; + } + continue; + } + } + return 0; +} + + +/** + * @brief reading a compressor decompression information file + * + * @param file_name file containing the compression configuration file + * @param info decompression information structure holding the read in + * information + * @param verbose_en print verbose output if not zero + * + * @returns 0 on success, error otherwise + */ + +int read_cmp_info(const char *file_name, struct cmp_info *info, int verbose_en) +{ + FILE *fp; + + if (!file_name) + abort(); + + if (!info) + abort(); + + if (strstr(file_name, ".cfg")) { + fprintf(stderr, "%s: %s: .cfg file extension found on decompression information file. You may have selected the wrong file.\n", + PROGRAM_NAME, file_name); + } + + fp = fopen(file_name, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, file_name, + strerror(errno)); + return -1; + } + + parse_info(fp, info); + + fclose(fp); + + if (verbose_en) { + printf("\n\n"); + print_cmp_info(info); + printf("\n"); + } + + return 0; +} + + +/** + * @brief reads a hex encoded uint8_t data (or model) file, returns a buffer + * containing the read in data + * + * @note data must be encode has 4 hex digits separated by a white space or new + * line character e.g. "ABCD 1234 AB12\n" + * + * @param file_name data file name + * @param samples amount of uint16_t data samples to read in + * @param verbose_en print verbose output if not zero + * + * @returns address to the buffer containing the read in data, NULL on error + */ + +uint8_t *read_file8(const char *file_name, uint32_t samples, int verbose_en) +{ + uint32_t i; + FILE *fp; + uint8_t *buffer; + char tmp_str[4]; /* 6 = 2 hex digit's + 1 white space + 1 \0 */ + + if (!file_name) + abort(); + + if (!samples) + return NULL; + + fp = fopen(file_name, "r"); + if (fp == NULL) { + fprintf(stderr, "%s: %s: %s\n", PROGRAM_NAME, file_name, + strerror(errno)); + return NULL; + } + + buffer = (uint8_t *)malloc(samples * SAM2BYT); + if (buffer == NULL) { + fprintf(stderr, "%s: Error allocating memory.\n", PROGRAM_NAME); + fclose(fp); + return NULL; + } + + for (i = 0; i < samples; i++) { + int j; + unsigned long read_val; + char *end; + + if (!fgets(tmp_str, sizeof(tmp_str), fp)) { + fprintf(stderr, "%s: %s: Error: The files does not contain enough data as given by the samples parameter.\n", + PROGRAM_NAME, file_name); + goto error; + } + + if (tmp_str[0] == '#' || tmp_str[0] == '\n') { + i--; /* a comment or empty line does not count as sample */ + continue; + } + + for (j = 0; j < 2; j++) { + if (!isxdigit(tmp_str[j])) { + fprintf(stderr, "%s: %s: Error: The data are not correct formatted. Expected format is like: 12 AB 23 CD .. ..\n", PROGRAM_NAME, + file_name); + goto error; + } + } + + if (!(isspace(tmp_str[2]) || tmp_str[2] == '\n')) { + fprintf(stderr, "%s: %s: Error: The data are not correct formatted. Expected format is: 12 AB 23 CD .. ..\n", PROGRAM_NAME, file_name); + goto error; + } + + read_val = strtoul(tmp_str, &end, 16); + + if (tmp_str == end || errno == ERANGE || read_val > UINT8_MAX) { + fprintf(stderr, "%s: %s: Error: The data can not be converted to integer.\n", + PROGRAM_NAME, file_name); + goto error; + } + + buffer[i] = (uint16_t)read_val; + + if (verbose_en) { + if (i == 0) + printf("\n\n"); + + printf("%02X", buffer[i]); + if (i && !((i + 1) % 28)) + printf("\n"); + else + printf(" "); + } + } + if (verbose_en) + printf("\n\n"); + + fgets(tmp_str, sizeof(tmp_str), fp); + if (!feof(fp) && tmp_str[0] == '\n') /* read last line break */ + fgets(tmp_str, sizeof(tmp_str), fp); + + if (!feof(fp)) { + fprintf(stderr, "%s: %s: Warning: The file may contain more data than specified by the samples parameter.\n", + PROGRAM_NAME, file_name); + } + + fclose(fp); + + return buffer; + + error: + free(buffer); + fclose(fp); + return NULL; +} + + +/** + * @brief reads a hex encoded uint16_t data (or model) file, returns a buffer + * containing the read in data + * + * @note data must be encode has 4 hex digits separated by a white space or new + * line character e.g. "ABCD 1234 AB12\n" + * + * @param file_name data file name + * @param samples amount of uint16_t data samples to read in + * @param verbose_en print verbose output if not zero + * + * @returns address to the buffer containing the read in data, NULL on error + */ + +uint16_t *read_file16(const char *file_name, uint32_t samples, int verbose_en) +{ + size_t i; + uint16_t *buf = (uint16_t *)read_file8(file_name, samples*2, verbose_en); + + if (!buf) + return NULL; + + for (i=0; i < samples; i++) + be16_to_cpus(&buf[i]); + + return buf; +} + + +/** + * @brief reads a hex encoded uint32_t data file, returns a buffer containing + * the read in data + * + * @note data must be encode has 2 hex digits separated by a white space or new + * line character e.g. AB CD 12 34 + * + * @param file_name data file name + * @param buf_len amount of uint32_t data samples to read in + * @param verbose_en print verbose output if not zero + * + * @returns address to the buffer containing the read in data, NULL on error + */ + +uint32_t *read_file32(const char *file_name, uint32_t buf_len, int verbose_en) +{ + size_t i; + + uint32_t *buf = (uint32_t *)read_file8(file_name, buf_len*sizeof(uint32_t), + verbose_en); + + if (!buf) + return NULL; + + for (i=0; i < buf_len; i++) + be32_to_cpus(&buf[i]); + + return buf; +} + + +/** + * @brief prints config struct + * + * @param cfg configuration to print + * @param rdcu_cfg if set additional RDCU parameter are printed as well + */ + +void print_cfg(const struct cmp_cfg *cfg, int rdcu_cfg) +{ + printf("#-------------------------------------------------------------------------------\n"); + printf("# Default Configuration File\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Selected compression mode\n"); + printf("# 0: raw mode\n"); + printf("# 1: model mode with zero escape symbol mechanism\n"); + printf("# 2: 1d differencing mode without input model with zero escape symbol mechanism\n"); + printf("# 3: model mode with multi escape symbol mechanism\n"); + printf("# 4: 1d differencing mode without input model multi escape symbol mechanism\n"); + printf("\n"); + printf("cmp_mode = %u\n", cfg->cmp_mode); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Number of samples (16 bit value) to compress, length of the data and model buffer\n"); + printf("\n"); + printf("samples = %u\n", cfg->samples); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Length of the compressed data buffer in number of samples (16 bit values)\n"); + printf("\n"); + printf("buffer_length = %u\n", cfg->buffer_length); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Golomb parameter for dictionary selection\n"); + printf("\n"); + printf("golomb_par = %u\n", cfg->golomb_par); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Spillover threshold for encoding outliers\n"); + printf("\n"); + printf("spill = %u\n", cfg->spill); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Model weighting parameter\n"); + printf("\n"); + printf("model_value = %u\n", cfg->model_value); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Number of noise bits to be rounded\n"); + printf("\n"); + printf("round = %u\n", cfg->round); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + + if (rdcu_cfg) { + printf("#-------------------------------------------------------------------------------\n"); + printf("# Hardware Compressor Settings (not need for SW compression)\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("\n"); + printf("# Adaptive 1 Golomb parameter; HW only\n"); + printf("\n"); + printf("ap1_golomb_par = %u\n", cfg->ap1_golomb_par); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Adaptive 1 spillover threshold; HW only\n"); + printf("\n"); + printf("ap1_spill = %u\n", cfg->ap1_spill); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Adaptive 2 Golomb parameter; HW only\n"); + printf("\n"); + printf("ap2_golomb_par = %u\n", cfg->ap2_golomb_par); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# Adaptive 2 spillover threshold; HW only\n"); + printf("\n"); + printf("ap2_spill = %u\n", cfg->ap2_spill); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# RDCU data to compress start address, the first data address in the RDCU SRAM; HW only\n"); + printf("\n"); + printf("rdcu_data_adr = 0x%06X\n", + cfg->rdcu_data_adr); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# RDCU model start address, the first model address in the RDCU SRAM\n"); + printf("\n"); + printf("rdcu_model_adr = 0x%06X\n", + cfg->rdcu_model_adr); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# RDCU updated model start address, the address in the RDCU SRAM where the updated model is stored\n"); + printf("\n"); + printf("rdcu_new_model_adr = 0x%06X\n", + cfg->rdcu_new_model_adr); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + printf("# RDCU compressed data start address, the first output data address in the RDCU SRAM\n"); + printf("\n"); + printf("rdcu_buffer_adr = 0x%06X\n", cfg->rdcu_buffer_adr); + printf("\n"); + printf("#-------------------------------------------------------------------------------\n"); + } +} + +/** + * @brief write a decompression information structure to a file + * + * @param info compressor information contains information of an executed + * compression + * @param output_prefix prefix of the written file (.info is added to the prefix) + * @param machine_cfg - if set write additional RDCU parameter in the file + * + * @returns 0 on success, error otherwise + */ + +int write_info(const struct cmp_info *info, const char *output_prefix, + int machine_cfg) +{ + FILE *fp = open_file(output_prefix, ".info"); + + if (fp == NULL) { + fprintf(stderr, "%s: %s%s: %s\n", PROGRAM_NAME, output_prefix, + ".info", strerror(errno)); + return -1; + } + + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Decompression Information File\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Compression mode used\n"); + fprintf(fp, "# 0: raw mode\n"); + fprintf(fp, "# 1: model mode with zero escape symbol mechanism\n"); + fprintf(fp, "# 2: 1d differencing mode without input model with zero escape symbol mechanism\n"); + fprintf(fp, "# 3: model mode with multi escape symbol mechanism\n"); + fprintf(fp, "# 4: 1d differencing mode without input model multi escape symbol mechanism\n"); + fprintf(fp, "\n"); + fprintf(fp, "cmp_mode_used = %u\n", info->cmp_mode_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Number of samples used, measured in 16 bit units, length of the data and model buffer\n"); + fprintf(fp, "\n"); + fprintf(fp, "samples_used = %u\n", info->samples_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Compressed data size; measured in bits\n"); + fprintf(fp, "\n"); + fprintf(fp, "cmp_size = %u\n", info->cmp_size); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Golomb parameter used\n"); + fprintf(fp, "\n"); + fprintf(fp, "golomb_par_used = %u\n", info->golomb_par_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Spillover threshold used\n"); + fprintf(fp, "\n"); + fprintf(fp, "spill_used = %u\n", info->spill_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Model weighting parameter used\n"); + fprintf(fp, "\n"); + fprintf(fp, "model_value_used = %u\n", info->model_value_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Number of noise bits to be rounded used\n"); + fprintf(fp, "\n"); + fprintf(fp, "round_used = %u\n", info->round_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + + if (machine_cfg) { + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Hardware Compressor Settings (not need for SW compression)\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "\n"); + fprintf(fp, "# Adaptive compressed data size 1; measured in bits\n"); + fprintf(fp, "\n"); + fprintf(fp, "ap1_cmp_size = %u\n", info->ap1_cmp_size); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Adaptive compressed data size 2; measured in bits\n"); + fprintf(fp, "\n"); + fprintf(fp, "ap2_cmp_size = %u\n", info->ap2_cmp_size); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, "# Updated model info start address used\n"); + fprintf(fp, "\n"); + fprintf(fp, "rdcu_new_model_adr_used = 0x%06X\n", info->rdcu_new_model_adr_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fprintf(fp, " #RDCU compressed data start address\n"); + fprintf(fp, "\n"); + fprintf(fp, "rdcu_cmp_adr_used = 0x%06X\n", info->rdcu_cmp_adr_used); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + } + fprintf(fp, "# Compressor errors\n"); + fprintf(fp, "\n"); + fprintf(fp, "# [bit 0] small_buffer_err; The length for the compressed data buffer is too small\n"); + fprintf(fp, "# [bit 1] cmp_mode_err; The cmp_mode parameter is not set correctly\n"); + fprintf(fp, "# [bit 2] model_value_err; The model_value parameter is not set correctly\n"); + fprintf(fp, "# [bit 3] cmp_par_err; The spill, golomb_par combination is not set correctly\n"); + fprintf(fp, "# [bit 4] ap1_cmp_par_err; The ap1_spill, ap1_golomb_par combination is not set correctly (only HW compression)\n"); + fprintf(fp, "# [bit 5] ap2_cmp_par_err; The ap2_spill, ap2_golomb_par combination is not set correctly (only HW compression)\n"); + fprintf(fp, "# [bit 6] mb_err; Multi bit error detected by the memory controller (only HW compression)\n"); + fprintf(fp, "# [bit 7] slave_busy_err; The bus master has received the 'slave busy' status (only HW compression)\n"); + fprintf(fp, "# [bit 8] slave_blocked_err; The bus master has received the “slave blocked” status (only HW compression)\n"); + fprintf(fp, "# [bit 9] invalid address_err; The bus master has received the “invalid address” status (only HW compression)\n"); + fprintf(fp, "\n"); + fprintf(fp, "cmp_err = %x\n", info->cmp_err); + fprintf(fp, "\n"); + fprintf(fp, "#-------------------------------------------------------------------------------\n"); + fclose(fp); + + return 0; +} diff --git a/test_data/test_data1.dat b/test_data/test_data1.dat new file mode 100644 index 0000000..a040702 --- /dev/null +++ b/test_data/test_data1.dat @@ -0,0 +1,4 @@ +01 00 00 0B C1 C1 7F 57 A6 00 0B C1 C1 7F 00 00 00 01 10 00 FF C0 04 80 04 82 04 83 04 80 04 85 +04 83 04 82 04 89 04 87 04 89 04 8A 04 7F 04 87 04 86 04 9A 04 DA 04 CD 04 8D 04 83 04 8D 04 9D +04 FD 04 E7 04 8F 04 84 04 7F 04 8B 04 9E 04 9A 04 89 04 90 04 84 04 7F 04 89 04 8E 04 80 04 82 +04 83 04 80 diff --git a/test_data/test_data2.dat b/test_data/test_data2.dat new file mode 100644 index 0000000..d891fd1 --- /dev/null +++ b/test_data/test_data2.dat @@ -0,0 +1,4 @@ +01 00 00 0B C1 C1 7F 57 A6 00 0B C1 C1 7F 00 00 00 01 10 00 FF C0 04 7F 04 81 04 84 04 83 04 81 +04 7D 04 85 04 84 04 85 04 8D 04 8B 04 81 04 83 04 8C 04 99 04 D8 04 CB 04 8E 04 85 04 8F 04 A1 +04 FF 04 ED 04 91 04 83 04 86 04 8D 04 A8 04 9B 04 85 04 90 04 7F 04 83 04 89 04 8A 04 86 04 81 +04 84 04 83 -- GitLab