Initial commit

This commit is contained in:
jaseg 2020-10-12 20:15:41 +02:00
commit e16ec19e3a
51 changed files with 4909 additions and 0 deletions

12
.gitmodules vendored Normal file
View file

@ -0,0 +1,12 @@
[submodule "lib/cmsis_device"]
path = lib/cmsis_device
url = https://github.com/STMicroelectronics/cmsis_device_f3
[submodule "lib/cmsis_core"]
path = lib/cmsis_core
url = https://github.com/STMicroelectronics/cmsis_core
[submodule "lib/hal_driver"]
path = lib/hal_driver
url = https://github.com/STMicroelectronics/stm32f3xx_hal_driver
[submodule "lib/musl"]
path = lib/musl
url = git://git.musl-libc.org/musl

160
Makefile Normal file
View file

@ -0,0 +1,160 @@
########################################################################################################################
# Dependency directories
########################################################################################################################
CMSIS_DEVICE_DIR ?= lib/cmsis_device
CMSIS_CORE_DIR ?= lib/cmsis_core
HAL_DIR ?= lib/hal_driver
MUSL_DIR ?= lib/musl
########################################################################################################################
# Sources
########################################################################################################################
C_SOURCES := src/main.c
C_SOURCES += src/cobs.c
C_SOURCES += src/clocks.c
C_SOURCES += src/bootloader.c
C_SOURCES += src/interrupts.c
C_SOURCES += src/system_stm32f3xx.c
C_SOURCES += config/fe_config.c
MUSL_SOURCES += string/strlen.c
C_SOURCES += $(addprefix $(MUSL_DIR)/src/,$(MUSL_SOURCES))
CXX_SOURCES +=
BUILDDIR ?= build
BINARY := fenris_f302r8tx.elf
LDSCRIPT := stm32f302r8tx.ld
########################################################################################################################
# Build parameters
########################################################################################################################
PREFIX ?= arm-none-eabi-
DEBUG ?= 1
CC := $(PREFIX)gcc
CXX := $(PREFIX)g++
LD := $(PREFIX)gcc
AR := $(PREFIX)ar
AS := $(PREFIX)as
SIZE := $(PREFIX)size
NM := $(PREFIX)nm
OBJCOPY := $(PREFIX)objcopy
OBJDUMP := $(PREFIX)objdump
GDB := $(PREFIX)gdb
HOST_CC ?= $(HOST_PREFIX)gcc
HOST_CXX ?= $(HOST_PREFIX)g++
HOST_LD ?= $(HOST_PREFIX)gcc
HOST_AR ?= $(HOST_PREFIX)ar
HOST_AS ?= $(HOST_PREFIX)as
HOST_OBJCOPY ?= $(HOST_PREFIX)objcopy
HOST_OBJDUMP ?= $(HOST_PREFIX)objdump
PYTHON3 ?= python3
DOT ?= dot
CMSIS_DIR_ABS := $(abspath $(CMSIS_DIR))
ARCH_FLAGS ?= -mthumb -mcpu=cortex-m4 -mfloat-abi=hard -mfpu=fpv4-sp-d16
SYSTEM_FLAGS ?= -nostdlib -ffreestanding -nostartfiles
CFLAGS += -Iinclude -Iconfig
CFLAGS += -I$(BUILDDIR)
CFLAGS += -I$(CMSIS_DEVICE_DIR)/Include -I$(CMSIS_CORE_DIR)/Include
CFLAGS += -I$(abspath include/musl_include_shims)
CFLAGS += -Os -std=gnu11 -g -DSTM32F302x8 -DSTM32F3 -DDEBUG=$(DEBUG)
CFLAGS += $(ARCH_FLAGS) $(SYSTEM_FLAGS)
CFLAGS += -fno-common -ffunction-sections -fdata-sections
# for musl
CFLAGS += -Dhidden=
CXXFLAGS += -Os -g
CXXFLAGS += $(ARCH_FLAGS) $(SYSTEM_FLAGS)
CXXFLAGS += -fno-common -ffunction-sections -fdata-sections
CXXFLAGS += -Wall -Wextra -Wshadow -Wundef -Wredundant-decls
CXXFLAGS += -I.
LDFLAGS += $(ARCH_FLAGS) $(SYSTEM_FLAGS)
LIBS += -lgcc
LDFLAGS += -Wl,--gc-sections
LINKMEM_FLAGS ?= --trim-stubs=startup_stm32f302x8.o --trace-sections .isr_vector --highlight-subdirs $(BUILDDIR)
OBJS := $(addprefix $(BUILDDIR)/,$(C_SOURCES:.c=.o) $(CXX_SOURCES:.cpp=.o))
ALL_OBJS := $(OBJS)
ALL_OBJS += $(BUILDDIR)/startup_stm32f302x8.o
########################################################################################################################
# Rules
########################################################################################################################
all: binsize
.PHONY: binsize
binsize: $(BUILDDIR)/$(BINARY) $(BUILDDIR)/$(BINARY:.elf=-symbol-sizes.pdf)
$(LD) -T$(LDSCRIPT) $(LDFLAGS) -Wl,--print-memory-usage -o /dev/null $(ALL_OBJS) $(LIBS)
@echo
@echo "▐▬▬▬▌ SyMbOL sIzE HiGhScORe LiSt ▐▬▬▬▌"
$(NM) --print-size --size-sort --radix=d $< | tail -n 20
$(BUILDDIR)/generated: ; mkdir -p $@
.PRECIOUS: $(BUILDDIR)/$(BINARY)
$(BUILDDIR)/$(BINARY) $(BUILDDIR)/$(BINARY:.elf=.map) &: $(ALL_OBJS)
$(LD) -T$(LDSCRIPT) $(LDFLAGS) -o $@ -Wl,-Map=$(BUILDDIR)/$(BINARY:.elf=.map) $^ $(LIBS)
build/$(BINARY:.elf=-symbol-sizes.dot): $(ALL_OBJS)
$(PYTHON3) tools/linkmem.py $(LINKMEM_FLAGS) $(LD) -T$(LDSCRIPT) $(LDFLAGS) $^ $(LIBS) > $@
%.pdf: %.dot
$(DOT) -T pdf $< -o $@
%.dot: %.elf
r2 -a arm -qc 'aa;agRd' $< 2>/dev/null >$@
$(BUILDDIR)/%.o: %.s
mkdir -p $(@D)
$(CC) $(COMMON_CFLAGS) $(CFLAGS) $(INT_CFLAGS) -o $@ -c $<
$(BUILDDIR)/src/%.o: src/%.c
mkdir -p $(@D)
$(CC) $(COMMON_CFLAGS) $(CFLAGS) $(INT_CFLAGS) -o $@ -c $<
$(BUILDDIR)/src/%.o: src/%.cpp
mkdir -p $(@D)
$(CXX) $(CXXFLAGS) -o $@ -c $<
$(BUILDDIR)/generated/%.o: $(BUILDDIR)/generated/%.c
mkdir -p $(@D)
$(CC) $(COMMON_CFLAGS) $(CFLAGS) $(INT_CFLAGS) -o $@ -c $<
$(BUILDDIR)/%.o: %.c
mkdir -p $(@D)
$(CC) $(COMMON_CFLAGS) $(CFLAGS) $(EXT_CFLAGS) -o $@ -c $<
clean:
rm -rf $(BUILDDIR)/src
rm -rf $(BUILDDIR)/generated
rm -f $(BUILDDIR)/$(BINARY)
rm -f $(BUILDDIR)/$(BINARY:.elf=.map)
rm -f $(BUILDDIR)/$(BINARY:.elf=-symbol-sizes.dot)
rm -f $(BUILDDIR)/$(BINARY:.elf=-symbol-sizes.pdf)
rm -f $(BUILDDIR)/tools/freq_meas_test
mrproper: clean
rm -rf build
.PHONY: clean mrproper
-include $(OBJS:.o=.d)

17
config/fe_config.c Normal file
View file

@ -0,0 +1,17 @@
#ifndef __FE_CONFIG_H__
#define __FE_CONFIG_H__
#include <fe_config_backend.h>
const struct fe_config_def fe_config = {
.bootloader_enable_pin = { FE_CONFIG_GPIOB, 0 },
.bootloader_enable_level = 1,
.signature_disable_pin = { FE_CONFIG_GPIOB, 1 },
.signature_disable_level = 1,
.usart = FE_CONFIG_USART1_PB7,
.baudrate = 115200,
/* The following string happens to be a valid COBS frame. */
.welcome_string = "Fenris Bootloader\r\n(c) 2020 jaseg\r\nSee https://git.jaseg.de/fenris \r\n",
};
#endif /* __FE_CONFIG_H__ */

22
include/bootloader.h Normal file
View file

@ -0,0 +1,22 @@
#ifndef __FE_BOOTLOADER_H__
#define __FE_BOOTLOADER_H__
#include <fe_global.h>
void fe_jump_to_application(void) __attribute__ ((noreturn));
void fe_system_reset(void) __attribute__ ((noreturn));
bool fe_check_img_valid(void);
void flash_unlock(void);
void flash_lock(void);
int flash_erase_page(size_t addr);
int flash_write(size_t addr, char *buf, size_t len);
int erase_user_flash(void);
#define PAGE_SIZE 0x2000
extern size_t flash_base;
extern size_t flash_size;
extern size_t bootloader_size;
#endif /* __FE_BOOTLOADER_H__ */

23
include/cobs.h Normal file
View file

@ -0,0 +1,23 @@
#ifndef __COBS_H__
#define __COBS_H__
#include <stdint.h>
#include <unistd.h>
#include <string.h>
struct cobs_decode_state {
size_t p;
size_t c;
};
ssize_t cobs_encode(char *dst, size_t dstlen, char *src, size_t srclen);
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen);
int cobs_encode_usart(int (*output)(void*, char), void *userdata, char *src, size_t srclen);
void cobs_decode_incremental_initialize(struct cobs_decode_state *state);
int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src);
#endif//__COBS_H__

14
include/fe_clocks.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef __FE_CLOCKS_H__
#define __FE_CLOCKS_H__
extern unsigned int sysclk_speed;
extern unsigned int ahb_speed;
extern unsigned int apb1_speed;
extern unsigned int apb2_speed;
extern unsigned int apb1_timer_speed;
extern unsigned int apb2_timer_speed;
void fe_config_clocks(void);
void delay_ms(int ms);
#endif /* __FE_CLOCKS_H__ */

View file

@ -0,0 +1,40 @@
#ifndef __FE_CONFIG_BACKEND_H__
#define __FE_CONFIG_BACKEND_H__
#include <fe_global.h>
#define FE_CONFIG_GPIOA RCC_AHBENR_GPIOAEN, GPIOA
#define FE_CONFIG_GPIOB RCC_AHBENR_GPIOBEN, GPIOB
#define FE_CONFIG_GPIOC RCC_AHBENR_GPIOCEN, GPIOC
#define FE_CONFIG_GPIOD RCC_AHBENR_GPIODEN, GPIOD
#define FE_CONFIG_GPIOF RCC_AHBENR_GPIOFEN, GPIOF
enum fe_config_usart {
FE_CONFIG_USART1_PB7,
FE_CONFIG_USART_COUNT
};
struct fe_config_gpiodef {
uint32_t rcc_ahbenr_flags;
GPIO_TypeDef *gpio;
int pin_number;
};
struct fe_config_def {
struct fe_config_gpiodef bootloader_enable_pin;
int bootloader_enable_level;
struct fe_config_gpiodef signature_disable_pin;
int signature_disable_level;
enum fe_config_usart usart;
int baudrate;
const char *welcome_string;
};
extern const struct fe_config_def fe_config;
void gpio_config(GPIO_TypeDef *gpio, int pin, int mode, int speed, int pullups, int alt);
#endif /* __FE_CONFIG_BACKEND_H__ */

19
include/fe_global.h Normal file
View file

@ -0,0 +1,19 @@
#ifndef __FE_GLOBAL_H__
#define __FE_GLOBAL_H__
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <sys/types.h>
#include <stm32f302x8.h>
#define UNUSED(x) ((void) x)
#define ARRAY_LENGTH(x) (sizeof(x) / sizeof(x[0]))
#define unused_a __attribute__((unused))
extern uint64_t sys_time_millis;
void __libc_init_array(void);
#endif /* __FE_GLOBAL_H__ */

14
include/fe_interrupts.h Normal file
View file

@ -0,0 +1,14 @@
#ifndef __FE_INTERRUPTS_H__
#define __FE_INTERRUPTS_H__
void NMI_Handler(void);
void HardFault_Handler(void);
void MemManage_Handler(void);
void BusFault_Handler(void);
void UsageFault_Handler(void);
void SVC_Handler(void);
void DebugMon_Handler(void);
void PendSV_Handler(void);
void SysTick_Handler(void);
#endif /* __FE_INTERRUPTS_H__ */

View file

@ -0,0 +1,23 @@
/* shim file for musl */
#ifndef __MUSL_SHIM_BITS_ALLTYPES_H__
#define __MUSL_SHIM_BITS_ALLTYPES_H__
#define _REDIR_TIME64 1
#define _Addr int
#define _Int64 long long
#define _Reg int
#define __BYTE_ORDER 1234
#define __LONG_MAX 0x7fffffffL
#ifndef __cplusplus
typedef unsigned wchar_t;
#endif
typedef float float_t;
typedef double double_t;
#endif /* __MUSL_SHIM_BITS_ALLTYPES_H__ */

View file

@ -0,0 +1,80 @@
#ifndef _ENDIAN_H
#define _ENDIAN_H
#include <features.h>
#define __NEED_uint16_t
#define __NEED_uint32_t
#define __NEED_uint64_t
#include <bits/alltypes.h>
#define __PDP_ENDIAN 3412
#define BIG_ENDIAN __BIG_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#define PDP_ENDIAN __PDP_ENDIAN
#define BYTE_ORDER __BYTE_ORDER
static __inline uint16_t __bswap16(uint16_t __x)
{
return __x<<8 | __x>>8;
}
static __inline uint32_t __bswap32(uint32_t __x)
{
return __x>>24 | __x>>8&0xff00 | __x<<8&0xff0000 | __x<<24;
}
static __inline uint64_t __bswap64(uint64_t __x)
{
return __bswap32(__x)+0ULL<<32 | __bswap32(__x>>32);
}
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define htobe16(x) __bswap16(x)
#define be16toh(x) __bswap16(x)
#define htobe32(x) __bswap32(x)
#define be32toh(x) __bswap32(x)
#define htobe64(x) __bswap64(x)
#define be64toh(x) __bswap64(x)
#define htole16(x) (uint16_t)(x)
#define le16toh(x) (uint16_t)(x)
#define htole32(x) (uint32_t)(x)
#define le32toh(x) (uint32_t)(x)
#define htole64(x) (uint64_t)(x)
#define le64toh(x) (uint64_t)(x)
#else
#define htobe16(x) (uint16_t)(x)
#define be16toh(x) (uint16_t)(x)
#define htobe32(x) (uint32_t)(x)
#define be32toh(x) (uint32_t)(x)
#define htobe64(x) (uint64_t)(x)
#define be64toh(x) (uint64_t)(x)
#define htole16(x) __bswap16(x)
#define le16toh(x) __bswap16(x)
#define htole32(x) __bswap32(x)
#define le32toh(x) __bswap32(x)
#define htole64(x) __bswap64(x)
#define le64toh(x) __bswap64(x)
#endif
#if defined(_GNU_SOURCE) || defined(_BSD_SOURCE)
#if __BYTE_ORDER == __LITTLE_ENDIAN
#define betoh16(x) __bswap16(x)
#define betoh32(x) __bswap32(x)
#define betoh64(x) __bswap64(x)
#define letoh16(x) (uint16_t)(x)
#define letoh32(x) (uint32_t)(x)
#define letoh64(x) (uint64_t)(x)
#else
#define betoh16(x) (uint16_t)(x)
#define betoh32(x) (uint32_t)(x)
#define betoh64(x) (uint64_t)(x)
#define letoh16(x) __bswap16(x)
#define letoh32(x) __bswap32(x)
#define letoh64(x) __bswap64(x)
#endif
#endif
#endif

View file

@ -0,0 +1,40 @@
#ifndef _FEATURES_H
#define _FEATURES_H
#if defined(_ALL_SOURCE) && !defined(_GNU_SOURCE)
#define _GNU_SOURCE 1
#endif
#if defined(_DEFAULT_SOURCE) && !defined(_BSD_SOURCE)
#define _BSD_SOURCE 1
#endif
#if !defined(_POSIX_SOURCE) && !defined(_POSIX_C_SOURCE) \
&& !defined(_XOPEN_SOURCE) && !defined(_GNU_SOURCE) \
&& !defined(_BSD_SOURCE) && !defined(__STRICT_ANSI__)
#define _BSD_SOURCE 1
#define _XOPEN_SOURCE 700
#endif
#if __STDC_VERSION__ >= 199901L
#define __restrict restrict
#elif !defined(__GNUC__)
#define __restrict
#endif
#if __STDC_VERSION__ >= 199901L || defined(__cplusplus)
#define __inline inline
#elif !defined(__GNUC__)
#define __inline
#endif
#if __STDC_VERSION__ >= 201112L
#elif defined(__GNUC__)
#define _Noreturn __attribute__((__noreturn__))
#else
#define _Noreturn
#endif
#define __REDIR(x,y) __typeof__(x) x __asm__(#y)
#endif

View file

@ -0,0 +1,6 @@
#ifndef __MUSL_SHIM_FP_ARCH_H__
#define __MUSL_SHIM_FP_ARCH_H__
#define hidden
#endif /* __MUSL_SHIM_FP_ARCH_H__ */

View file

@ -0,0 +1,270 @@
#ifndef _LIBM_H
#define _LIBM_H
#include <stdint.h>
#include <float.h>
#include <math.h>
#include <endian.h>
#include "fp_arch.h"
#if LDBL_MANT_DIG == 53 && LDBL_MAX_EXP == 1024
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
union ldshape {
long double f;
struct {
uint64_t m;
uint16_t se;
} i;
};
#elif LDBL_MANT_DIG == 64 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
/* This is the m68k variant of 80-bit long double, and this definition only works
* on archs where the alignment requirement of uint64_t is <= 4. */
union ldshape {
long double f;
struct {
uint16_t se;
uint16_t pad;
uint64_t m;
} i;
};
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __LITTLE_ENDIAN
union ldshape {
long double f;
struct {
uint64_t lo;
uint32_t mid;
uint16_t top;
uint16_t se;
} i;
struct {
uint64_t lo;
uint64_t hi;
} i2;
};
#elif LDBL_MANT_DIG == 113 && LDBL_MAX_EXP == 16384 && __BYTE_ORDER == __BIG_ENDIAN
union ldshape {
long double f;
struct {
uint16_t se;
uint16_t top;
uint32_t mid;
uint64_t lo;
} i;
struct {
uint64_t hi;
uint64_t lo;
} i2;
};
#else
#error Unsupported long double representation
#endif
/* Support non-nearest rounding mode. */
#define WANT_ROUNDING 1
/* Support signaling NaNs. */
#define WANT_SNAN 0
#if WANT_SNAN
#error SNaN is unsupported
#else
#define issignalingf_inline(x) 0
#define issignaling_inline(x) 0
#endif
#ifndef TOINT_INTRINSICS
#define TOINT_INTRINSICS 0
#endif
#if TOINT_INTRINSICS
/* Round x to nearest int in all rounding modes, ties have to be rounded
consistently with converttoint so the results match. If the result
would be outside of [-2^31, 2^31-1] then the semantics is unspecified. */
static double_t roundtoint(double_t);
/* Convert x to nearest int in all rounding modes, ties have to be rounded
consistently with roundtoint. If the result is not representible in an
int32_t then the semantics is unspecified. */
static int32_t converttoint(double_t);
#endif
/* Helps static branch prediction so hot path can be better optimized. */
#ifdef __GNUC__
#define predict_true(x) __builtin_expect(!!(x), 1)
#define predict_false(x) __builtin_expect(x, 0)
#else
#define predict_true(x) (x)
#define predict_false(x) (x)
#endif
/* Evaluate an expression as the specified type. With standard excess
precision handling a type cast or assignment is enough (with
-ffloat-store an assignment is required, in old compilers argument
passing and return statement may not drop excess precision). */
static inline float eval_as_float(float x)
{
float y = x;
return y;
}
static inline double eval_as_double(double x)
{
double y = x;
return y;
}
/* fp_barrier returns its input, but limits code transformations
as if it had a side-effect (e.g. observable io) and returned
an arbitrary value. */
#ifndef fp_barrierf
#define fp_barrierf fp_barrierf
static inline float fp_barrierf(float x)
{
volatile float y = x;
return y;
}
#endif
#ifndef fp_barrier
#define fp_barrier fp_barrier
static inline double fp_barrier(double x)
{
volatile double y = x;
return y;
}
#endif
#ifndef fp_barrierl
#define fp_barrierl fp_barrierl
static inline long double fp_barrierl(long double x)
{
volatile long double y = x;
return y;
}
#endif
/* fp_force_eval ensures that the input value is computed when that's
otherwise unused. To prevent the constant folding of the input
expression, an additional fp_barrier may be needed or a compilation
mode that does so (e.g. -frounding-math in gcc). Then it can be
used to evaluate an expression for its fenv side-effects only. */
#ifndef fp_force_evalf
#define fp_force_evalf fp_force_evalf
static inline void fp_force_evalf(float x)
{
volatile float y;
y = x;
}
#endif
#ifndef fp_force_eval
#define fp_force_eval fp_force_eval
static inline void fp_force_eval(double x)
{
volatile double y;
y = x;
}
#endif
#ifndef fp_force_evall
#define fp_force_evall fp_force_evall
static inline void fp_force_evall(long double x)
{
volatile long double y;
y = x;
}
#endif
#define FORCE_EVAL(x) do { \
if (sizeof(x) == sizeof(float)) { \
fp_force_evalf(x); \
} else if (sizeof(x) == sizeof(double)) { \
fp_force_eval(x); \
} else { \
fp_force_evall(x); \
} \
} while(0)
#define asuint(f) ((union{float _f; uint32_t _i;}){f})._i
#define asfloat(i) ((union{uint32_t _i; float _f;}){i})._f
#define asuint64(f) ((union{double _f; uint64_t _i;}){f})._i
#define asdouble(i) ((union{uint64_t _i; double _f;}){i})._f
#define EXTRACT_WORDS(hi,lo,d) \
do { \
uint64_t __u = asuint64(d); \
(hi) = __u >> 32; \
(lo) = (uint32_t)__u; \
} while (0)
#define GET_HIGH_WORD(hi,d) \
do { \
(hi) = asuint64(d) >> 32; \
} while (0)
#define GET_LOW_WORD(lo,d) \
do { \
(lo) = (uint32_t)asuint64(d); \
} while (0)
#define INSERT_WORDS(d,hi,lo) \
do { \
(d) = asdouble(((uint64_t)(hi)<<32) | (uint32_t)(lo)); \
} while (0)
#define SET_HIGH_WORD(d,hi) \
INSERT_WORDS(d, hi, (uint32_t)asuint64(d))
#define SET_LOW_WORD(d,lo) \
INSERT_WORDS(d, asuint64(d)>>32, lo)
#define GET_FLOAT_WORD(w,d) \
do { \
(w) = asuint(d); \
} while (0)
#define SET_FLOAT_WORD(d,w) \
do { \
(d) = asfloat(w); \
} while (0)
hidden int __rem_pio2_large(double*,double*,int,int,int);
hidden int __rem_pio2(double,double*);
hidden double __sin(double,double,int);
hidden double __cos(double,double);
hidden double __tan(double,double,int);
hidden double __expo2(double);
hidden int __rem_pio2f(float,double*);
hidden float __sindf(double);
hidden float __cosdf(double);
hidden float __tandf(double,int);
hidden float __expo2f(float);
hidden int __rem_pio2l(long double, long double *);
hidden long double __sinl(long double, long double, int);
hidden long double __cosl(long double, long double);
hidden long double __tanl(long double, long double, int);
hidden long double __polevll(long double, const long double *, int);
hidden long double __p1evll(long double, const long double *, int);
hidden double __lgamma_r(double, int *);
hidden float __lgammaf_r(float, int *);
/* error handling functions */
hidden float __math_xflowf(uint32_t, float);
hidden float __math_uflowf(uint32_t);
hidden float __math_oflowf(uint32_t);
hidden float __math_divzerof(uint32_t);
hidden float __math_invalidf(float);
hidden double __math_xflow(uint32_t, double);
hidden double __math_uflow(uint32_t);
hidden double __math_oflow(uint32_t);
hidden double __math_divzero(uint32_t);
hidden double __math_invalid(double);
#endif

337
include/stm32_hal_conf.h Normal file
View file

@ -0,0 +1,337 @@
/**
******************************************************************************
* @file stm32f3xx_hal_conf.h
* @author MCD Application Team
* @brief HAL configuration file.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/* Define to prevent recursive inclusion -------------------------------------*/
#ifndef __STM32F3xx_HAL_CONF_H
#define __STM32F3xx_HAL_CONF_H
#ifdef __cplusplus
extern "C" {
#endif
/* Exported types ------------------------------------------------------------*/
/* Exported constants --------------------------------------------------------*/
/* ########################## Module Selection ############################## */
/**
* @brief This is the list of modules to be used in the HAL driver
*/
#define HAL_MODULE_ENABLED
// #define HAL_ADC_MODULE_ENABLED
// #define HAL_CAN_MODULE_ENABLED
/* #define HAL_CAN_LEGACY_MODULE_ENABLED */
// #define HAL_CEC_MODULE_ENABLED
// #define HAL_COMP_MODULE_ENABLED
// #define HAL_CORTEX_MODULE_ENABLED
// #define HAL_CRC_MODULE_ENABLED
// #define HAL_DAC_MODULE_ENABLED
// #define HAL_DMA_MODULE_ENABLED
#define HAL_FLASH_MODULE_ENABLED
// #define HAL_GPIO_MODULE_ENABLED
// #define HAL_EXTI_MODULE_ENABLED
// #define HAL_HRTIM_MODULE_ENABLED
// #define HAL_I2C_MODULE_ENABLED
// #define HAL_I2S_MODULE_ENABLED
// #define HAL_IRDA_MODULE_ENABLED
// #define HAL_IWDG_MODULE_ENABLED
// #define HAL_OPAMP_MODULE_ENABLED
// #define HAL_PCD_MODULE_ENABLED
// #define HAL_PWR_MODULE_ENABLED
// #define HAL_RCC_MODULE_ENABLED
// #define HAL_RTC_MODULE_ENABLED
// #define HAL_SDADC_MODULE_ENABLED
// #define HAL_SMARTCARD_MODULE_ENABLED
// #define HAL_SMBUS_MODULE_ENABLED
// #define HAL_SPI_MODULE_ENABLED
// #define HAL_TIM_MODULE_ENABLED
// #define HAL_TSC_MODULE_ENABLED
// #define HAL_UART_MODULE_ENABLED
// #define HAL_USART_MODULE_ENABLED
// #define HAL_WWDG_MODULE_ENABLED
/* ########################## HSE/HSI Values adaptation ##################### */
/**
* @brief Adjust the value of External High Speed oscillator (HSE) used in your application.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSE is used as system clock source, directly or through the PLL).
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE (8000000U) /*!< Value of the External oscillator in Hz */
#endif /* HSE_VALUE */
/**
* @brief In the following line adjust the External High Speed oscillator (HSE) Startup
* Timeout value
*/
#if !defined (HSE_STARTUP_TIMEOUT)
#define HSE_STARTUP_TIMEOUT (100U) /*!< Time out for HSE start up, in ms */
#endif /* HSE_STARTUP_TIMEOUT */
/**
* @brief Internal High Speed oscillator (HSI) value.
* This value is used by the RCC HAL module to compute the system frequency
* (when HSI is used as system clock source, directly or through the PLL).
*/
#if !defined (HSI_VALUE)
#define HSI_VALUE (8000000U) /*!< Value of the Internal oscillator in Hz*/
#endif /* HSI_VALUE */
/**
* @brief In the following line adjust the Internal High Speed oscillator (HSI) Startup
* Timeout value
*/
#if !defined (HSI_STARTUP_TIMEOUT)
#define HSI_STARTUP_TIMEOUT (5000U) /*!< Time out for HSI start up */
#endif /* HSI_STARTUP_TIMEOUT */
/**
* @brief Internal Low Speed oscillator (LSI) value.
*/
#if !defined (LSI_VALUE)
#define LSI_VALUE (40000U)
#endif /* LSI_VALUE */ /*!< Value of the Internal Low Speed oscillator in Hz
The real value may vary depending on the variations
in voltage and temperature. */
/**
* @brief External Low Speed oscillator (LSE) value.
*/
#if !defined (LSE_VALUE)
#define LSE_VALUE (32768U) /*!< Value of the External Low Speed oscillator in Hz */
#endif /* LSE_VALUE */
/**
* @brief Time out for LSE start up value in ms.
*/
#if !defined (LSE_STARTUP_TIMEOUT)
#define LSE_STARTUP_TIMEOUT (5000U) /*!< Time out for LSE start up, in ms */
#endif /* LSE_STARTUP_TIMEOUT */
/**
* @brief External clock source for I2S peripheral
* This value is used by the I2S HAL module to compute the I2S clock source
* frequency, this source is inserted directly through I2S_CKIN pad.
* - External clock generated through external PLL component on EVAL 303 (based on MCO or crystal)
* - External clock not generated on EVAL 373
*/
#if !defined (EXTERNAL_CLOCK_VALUE)
#define EXTERNAL_CLOCK_VALUE (8000000U) /*!< Value of the External oscillator in Hz*/
#endif /* EXTERNAL_CLOCK_VALUE */
/* Tip: To avoid modifying this file each time you need to use different HSE,
=== you can define the HSE value in your toolchain compiler preprocessor. */
/* ########################### System Configuration ######################### */
/**
* @brief This is the HAL system configuration section
*/
#define VDD_VALUE (3300U) /*!< Value of VDD in mv */
#define TICK_INT_PRIORITY ((uint32_t)(1U<<__NVIC_PRIO_BITS) - 1U) /*!< tick interrupt priority (lowest by default) */
#define USE_RTOS 0U
#define PREFETCH_ENABLE 1U
#define INSTRUCTION_CACHE_ENABLE 0U
#define DATA_CACHE_ENABLE 0U
#define USE_SPI_CRC 1U
#define USE_HAL_ADC_REGISTER_CALLBACKS 0U /* ADC register callback disabled */
#define USE_HAL_CAN_REGISTER_CALLBACKS 0U /* CAN register callback disabled */
#define USE_HAL_COMP_REGISTER_CALLBACKS 0U /* COMP register callback disabled */
#define USE_HAL_CEC_REGISTER_CALLBACKS 0U /* CEC register callback disabled */
#define USE_HAL_DAC_REGISTER_CALLBACKS 0U /* DAC register callback disabled */
#define USE_HAL_SRAM_REGISTER_CALLBACKS 0U /* SRAM register callback disabled */
#define USE_HAL_SMBUS_REGISTER_CALLBACKS 0U /* SMBUS register callback disabled */
#define USE_HAL_SDADC_REGISTER_CALLBACKS 0U /* SDADC register callback disabled */
#define USE_HAL_NAND_REGISTER_CALLBACKS 0U /* NAND register callback disabled */
#define USE_HAL_NOR_REGISTER_CALLBACKS 0U /* NOR register callback disabled */
#define USE_HAL_PCCARD_REGISTER_CALLBACKS 0U /* PCCARD register callback disabled */
#define USE_HAL_HRTIM_REGISTER_CALLBACKS 0U /* HRTIM register callback disabled */
#define USE_HAL_I2C_REGISTER_CALLBACKS 0U /* I2C register callback disabled */
#define USE_HAL_UART_REGISTER_CALLBACKS 0U /* UART register callback disabled */
#define USE_HAL_USART_REGISTER_CALLBACKS 0U /* USART register callback disabled */
#define USE_HAL_IRDA_REGISTER_CALLBACKS 0U /* IRDA register callback disabled */
#define USE_HAL_SMARTCARD_REGISTER_CALLBACKS 0U /* SMARTCARD register callback disabled */
#define USE_HAL_WWDG_REGISTER_CALLBACKS 0U /* WWDG register callback disabled */
#define USE_HAL_OPAMP_REGISTER_CALLBACKS 0U /* OPAMP register callback disabled */
#define USE_HAL_RTC_REGISTER_CALLBACKS 0U /* RTC register callback disabled */
#define USE_HAL_SPI_REGISTER_CALLBACKS 0U /* SPI register callback disabled */
#define USE_HAL_I2S_REGISTER_CALLBACKS 0U /* I2S register callback disabled */
#define USE_HAL_TIM_REGISTER_CALLBACKS 0U /* TIM register callback disabled */
#define USE_HAL_TSC_REGISTER_CALLBACKS 0U /* TSC register callback disabled */
#define USE_HAL_PCD_REGISTER_CALLBACKS 0U /* PCD register callback disabled */
/* ########################## Assert Selection ############################## */
/**
* @brief Uncomment the line below to expanse the "assert_param" macro in the
* HAL drivers code
*/
/*#define USE_FULL_ASSERT 1*/
/* Includes ------------------------------------------------------------------*/
/**
* @brief Include module's header file
*/
#ifdef HAL_RCC_MODULE_ENABLED
#include "stm32f3xx_hal_rcc.h"
#endif /* HAL_RCC_MODULE_ENABLED */
#ifdef HAL_GPIO_MODULE_ENABLED
#include "stm32f3xx_hal_gpio.h"
#endif /* HAL_GPIO_MODULE_ENABLED */
#ifdef HAL_EXTI_MODULE_ENABLED
#include "stm32f3xx_hal_exti.h"
#endif /* HAL_EXTI_MODULE_ENABLED */
#ifdef HAL_DMA_MODULE_ENABLED
#include "stm32f3xx_hal_dma.h"
#endif /* HAL_DMA_MODULE_ENABLED */
#ifdef HAL_CORTEX_MODULE_ENABLED
#include "stm32f3xx_hal_cortex.h"
#endif /* HAL_CORTEX_MODULE_ENABLED */
#ifdef HAL_ADC_MODULE_ENABLED
#include "stm32f3xx_hal_adc.h"
#endif /* HAL_ADC_MODULE_ENABLED */
#ifdef HAL_CAN_MODULE_ENABLED
#include "stm32f3xx_hal_can.h"
#endif /* HAL_CAN_MODULE_ENABLED */
#ifdef HAL_CAN_LEGACY_MODULE_ENABLED
#include "stm32f3xx_hal_can_legacy.h"
#endif /* HAL_CAN_LEGACY_MODULE_ENABLED */
#ifdef HAL_CEC_MODULE_ENABLED
#include "stm32f3xx_hal_cec.h"
#endif /* HAL_CEC_MODULE_ENABLED */
#ifdef HAL_COMP_MODULE_ENABLED
#include "stm32f3xx_hal_comp.h"
#endif /* HAL_COMP_MODULE_ENABLED */
#ifdef HAL_CRC_MODULE_ENABLED
#include "stm32f3xx_hal_crc.h"
#endif /* HAL_CRC_MODULE_ENABLED */
#ifdef HAL_DAC_MODULE_ENABLED
#include "stm32f3xx_hal_dac.h"
#endif /* HAL_DAC_MODULE_ENABLED */
#ifdef HAL_FLASH_MODULE_ENABLED
#include "stm32f3xx_hal_flash.h"
#endif /* HAL_FLASH_MODULE_ENABLED */
#ifdef HAL_HRTIM_MODULE_ENABLED
#include "stm32f3xx_hal_hrtim.h"
#endif /* HAL_HRTIM_MODULE_ENABLED */
#ifdef HAL_I2C_MODULE_ENABLED
#include "stm32f3xx_hal_i2c.h"
#endif /* HAL_I2C_MODULE_ENABLED */
#ifdef HAL_I2S_MODULE_ENABLED
#include "stm32f3xx_hal_i2s.h"
#endif /* HAL_I2S_MODULE_ENABLED */
#ifdef HAL_IRDA_MODULE_ENABLED
#include "stm32f3xx_hal_irda.h"
#endif /* HAL_IRDA_MODULE_ENABLED */
#ifdef HAL_IWDG_MODULE_ENABLED
#include "stm32f3xx_hal_iwdg.h"
#endif /* HAL_IWDG_MODULE_ENABLED */
#ifdef HAL_OPAMP_MODULE_ENABLED
#include "stm32f3xx_hal_opamp.h"
#endif /* HAL_OPAMP_MODULE_ENABLED */
#ifdef HAL_PCD_MODULE_ENABLED
#include "stm32f3xx_hal_pcd.h"
#endif /* HAL_PCD_MODULE_ENABLED */
#ifdef HAL_PWR_MODULE_ENABLED
#include "stm32f3xx_hal_pwr.h"
#endif /* HAL_PWR_MODULE_ENABLED */
#ifdef HAL_RTC_MODULE_ENABLED
#include "stm32f3xx_hal_rtc.h"
#endif /* HAL_RTC_MODULE_ENABLED */
#ifdef HAL_SDADC_MODULE_ENABLED
#include "stm32f3xx_hal_sdadc.h"
#endif /* HAL_SDADC_MODULE_ENABLED */
#ifdef HAL_SMARTCARD_MODULE_ENABLED
#include "stm32f3xx_hal_smartcard.h"
#endif /* HAL_SMARTCARD_MODULE_ENABLED */
#ifdef HAL_SMBUS_MODULE_ENABLED
#include "stm32f3xx_hal_smbus.h"
#endif /* HAL_SMBUS_MODULE_ENABLED */
#ifdef HAL_SPI_MODULE_ENABLED
#include "stm32f3xx_hal_spi.h"
#endif /* HAL_SPI_MODULE_ENABLED */
#ifdef HAL_TIM_MODULE_ENABLED
#include "stm32f3xx_hal_tim.h"
#endif /* HAL_TIM_MODULE_ENABLED */
#ifdef HAL_TSC_MODULE_ENABLED
#include "stm32f3xx_hal_tsc.h"
#endif /* HAL_TSC_MODULE_ENABLED */
#ifdef HAL_UART_MODULE_ENABLED
#include "stm32f3xx_hal_uart.h"
#endif /* HAL_UART_MODULE_ENABLED */
#ifdef HAL_USART_MODULE_ENABLED
#include "stm32f3xx_hal_usart.h"
#endif /* HAL_USART_MODULE_ENABLED */
#ifdef HAL_WWDG_MODULE_ENABLED
#include "stm32f3xx_hal_wwdg.h"
#endif /* HAL_WWDG_MODULE_ENABLED */
/* Exported macro ------------------------------------------------------------*/
#ifdef USE_FULL_ASSERT
/**
* @brief The assert_param macro is used for function's parameters check.
* @param expr: If expr is false, it calls assert_failed function
* which reports the name of the source file and the source
* line number of the call that failed.
* If expr is true, it returns no value.
* @retval None
*/
#define assert_param(expr) ((expr) ? (void)0U : assert_failed((uint8_t *)__FILE__, __LINE__))
/* Exported functions ------------------------------------------------------- */
void assert_failed(uint8_t* file, uint32_t line);
#else
#define assert_param(expr) ((void)0U)
#endif /* USE_FULL_ASSERT */
#ifdef __cplusplus
}
#endif
#endif /* __STM32F3xx_HAL_CONF_H */
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

1
lib/cmsis_core Submodule

@ -0,0 +1 @@
Subproject commit 96d6da4e252b06dcfdc041e7df23e86161c33007

1
lib/cmsis_device Submodule

@ -0,0 +1 @@
Subproject commit 5e4ee5ed7a7b6c85176bb70a9fd3c72d6eb99f1b

1
lib/hal_driver Submodule

@ -0,0 +1 @@
Subproject commit 1761b6207318ede021706e75aae78f452d72b6fa

1
lib/musl Submodule

@ -0,0 +1 @@
Subproject commit a5aff1972c9e3981566414b09a28e331ccd2be5d

99
src/bootloader.c Normal file
View file

@ -0,0 +1,99 @@
#include <fe_global.h>
#include <fe_config_backend.h>
#include <bootloader.h>
#include <core_cm4.h>
typedef void (*void_func)(void);
extern int _Flash_Base;
size_t flash_base = (size_t)&_Flash_Base;
extern int _Flash_Size;
size_t flash_size = (size_t)&_Flash_Size;
extern int _Bootloader_Size;
size_t bootloader_size = (size_t)&_Bootloader_Size;
bool fe_check_img_valid(void) {
uint32_t *end_of_flash = (uint32_t *)(flash_base + flash_size);
end_of_flash -= 1;
return *end_of_flash != 0xffffffff;
}
void fe_jump_to_application() {
uint32_t *user_isr_vector = (uint32_t *)flash_base;
void_func user_reset_vector = (void_func)user_isr_vector[1];
SCB->VTOR = (uint32_t)flash_base;
__set_MSP(user_isr_vector[0]);
user_reset_vector();
}
void fe_system_reset(void) {
SCB->AIRCR |= SCB_AIRCR_SYSRESETREQ_Msk;
}
void flash_unlock() {
FLASH->KEYR = 0x45670123;
FLASH->KEYR = 0xCDEF89AB;
}
void flash_lock() {
FLASH->CR |= FLASH_CR_LOCK;
}
int flash_erase_page(size_t addr) {
while (FLASH->SR & FLASH_SR_BSY)
;
FLASH->CR |= FLASH_CR_PER;
FLASH->AR = addr;
FLASH->CR |= FLASH_CR_STRT;
/* RM0365, pg. 63: The software should start checking if the BSY bit equals 0 at least one CPU cycle after setting
* the STRT bit */
asm volatile ("nop");
while (FLASH->SR & FLASH_SR_BSY)
;
if (FLASH->SR & FLASH_SR_EOP) {
FLASH->SR = FLASH_SR_EOP;
return 0;
}
return -1;
}
int flash_write(size_t addr, char *buf, size_t len) {
assert((len&1) == 0);
assert((addr&1) == 0);
uint16_t *dst = (uint16_t *)addr;
uint16_t *src = (uint16_t *)src;
len /= 2;
while (len--) {
*dst++ = *src++;
while (FLASH->SR & FLASH_SR_BSY)
;
}
if (FLASH->SR & FLASH_SR_EOP)
FLASH->SR = FLASH_SR_EOP;
else
return 1;
return 0;
}
int erase_user_flash() {
assert ((_Bootloader_Size & (PAGE_SIZE-1)) == 0);
size_t first_page = _Flash_Base + _Bootloader_Size;
size_t npages = (_Flash_Size - _Bootloader_Size) / PAGE_SIZE;
int rc = 0;
while (npages--)
rc |= flash_erase_page(first_page + npages); /* TODO error handling */
return rc;
}

83
src/clocks.c Normal file
View file

@ -0,0 +1,83 @@
#include <fe_global.h>
#include <fe_clocks.h>
unsigned int sysclk_speed;
unsigned int ahb_speed;
unsigned int apb1_speed;
unsigned int apb2_speed;
unsigned int apb1_timer_speed;
unsigned int apb2_timer_speed;
void delay_ms(int ms) {
uint32_t init_val = SysTick->VAL;
uint32_t wait_end = sys_time_millis + ms;
while (sys_time_millis < wait_end)
;
while (SysTick->VAL >= init_val) {
if (sys_time_millis > wait_end)
return;
}
}
void fe_config_clocks()
{
/* 8MHz HSI clock as PLL source. */
#define HSI_SPEED 8000000
/* PLL output = HSI / 2 * PLL_MUL */
#define PLL_MUL 16
/* Check that we came out of reset correctly */
if (((RCC->CFGR & RCC_CFGR_SWS_Msk) >> RCC_CFGR_SW_Pos) != 0)
asm volatile ("bkpt");
if (RCC->CR & RCC_CR_HSEON)
asm volatile ("bkpt");
if (RCC->CR & RCC_CR_PLLON)
asm volatile ("bkpt");
RCC->CFGR = 0;
RCC->CFGR |= (0<<RCC_CFGR_PLLSRC_Pos); /* PLL input: HSI /2 */
RCC->CFGR |= ((PLL_MUL-2)<<RCC_CFGR_PLLMUL_Pos);
sysclk_speed = HSI_SPEED / 2 * PLL_MUL;
/* set AHB prescaler to /1 */
RCC->CFGR |= (0 << RCC_CFGR_HPRE_Pos);
ahb_speed = sysclk_speed;
/* set ABP1 prescaler to 2 -> 36MHz */
RCC->CFGR |= (4 << RCC_CFGR_PPRE1_Pos);
apb1_speed = sysclk_speed / 2;
apb1_timer_speed = apb1_speed * 2;
/* set ABP2 prescaler to 2 -> 36MHz */
RCC->CFGR |= (4 << RCC_CFGR_PPRE2_Pos);
apb2_speed = sysclk_speed / 2;
apb2_timer_speed = apb2_speed * 2;
/* Configure PLL */
RCC->CR |= RCC_CR_PLLON;
/* Wait for main PLL */
while(!(RCC->CR & RCC_CR_PLLRDY))
;
/* Configure Flash: enable prefetch; set latency = 2 wait states
* See reference manual (RM0365), Section 4.5.1
*/
FLASH->ACR = FLASH_ACR_PRFTBE | (2<<FLASH_ACR_LATENCY_Pos);
/* Select PLL as system clock source */
RCC->CFGR &= ~RCC_CFGR_SW_Msk;
RCC->CFGR |= 2 << RCC_CFGR_SW_Pos;
/* Wait for clock to switch over */
while ((RCC->CFGR & RCC_CFGR_SWS_Msk)>>RCC_CFGR_SWS_Pos != 2)
;
SystemCoreClockUpdate();
SysTick_Config(SystemCoreClock / 1000); /* 1 ms ticks */
NVIC_SetPriority(SysTick_IRQn, 32);
}

211
src/cobs.c Normal file
View file

@ -0,0 +1,211 @@
#include "cobs.h"
int cobs_encode_usart(int (*output)(void*, char), void *userdata, char *src, size_t srclen) {
if (srclen > 254)
return -1;
size_t p = 0;
while (p <= srclen) {
char val;
if (p != 0 && src[p-1] != 0) {
val = src[p-1];
} else {
size_t q = p;
while (q < srclen && src[q] != 0)
q++;
val = (char)q-p+1;
}
int rv = output(userdata, val);
if (rv)
return rv;
p++;
}
int rv = output(userdata, 0);
if (rv)
return rv;
return 0;
}
/*@ requires \valid(dst + (0..dstlen-1));
@ requires \valid_read(src + (0..srclen-1));
@ requires \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@
@ behavior maybe_valid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes \exists integer j; j > 0 && \forall integer i; 0 <= i < j ==> src[i] != 0;
@ assumes \exists integer i; 0 <= i < srclen && src[i] == 0;
@ assigns dst[0..dstlen-1];
@ ensures \result >= 0 || \result == -3;
@ ensures \result >= 0 ==> src[\result+1] == 0;
@ ensures \result >= 0 ==> (\forall integer i; 0 <= i < \result ==> src[i] != 0);
@
@ behavior invalid_frame:
@ assumes 1 <= srclen <= dstlen <= 65535;
@ assumes src[0] == 0 || \forall integer i; 0 <= i < srclen ==> src[i] != 0;
@ assigns dst[0..dstlen-1];
@ ensures \result == -2;
@
@ behavior invalid_buffers:
@ assumes dstlen < 0 || dstlen > 65535
@ || srclen < 1 || srclen > 65535
@ || dstlen < srclen;
@ assigns \nothing;
@ ensures \result == -1;
@
@ complete behaviors;
@ disjoint behaviors;
@*/
ssize_t cobs_decode(char *dst, size_t dstlen, char *src, size_t srclen) {
if (dstlen > 65535 || srclen > 65535)
return -1;
if (srclen < 1)
return -1;
if (dstlen < srclen)
return -1;
size_t p = 1;
size_t c = (unsigned char)src[0];
//@ assert 0 <= c < 256;
//@ assert 0 <= c;
//@ assert c < 256;
if (c == 0)
return -2; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c <= 257;
//@ assert c > 0;
//@ assert c >= 0 && c != 0 ==> c > 0;
/*@ //loop invariant \forall integer i; 0 <= i <= p ==> (i == srclen || src[i] != 0);
@ loop invariant \forall integer i; 1 <= i < p ==> src[i] != 0;
@ loop invariant c > 0;
@ loop invariant 1 <= p <= srclen <= dstlen <= 65535;
@ loop invariant \separated(dst + (0..dstlen-1), src + (0..srclen-1));
@ loop invariant \valid_read(src + (0..srclen-1));
@ loop invariant \forall integer i; 1 <= i <= srclen ==> \valid(dst + i - 1);
@ loop assigns dst[0..dstlen-1], p, c;
@ loop variant srclen-p;
@*/
while (p < srclen && src[p]) {
char val;
c--;
//@ assert src[p] != 0;
if (c == 0) {
c = (unsigned char)src[p];
val = 0;
} else {
val = src[p];
}
//@ assert 0 <= p-1 <= dstlen-1;
dst[p-1] = val;
p++;
}
if (p == srclen)
return -2; /* Invalid framing. The terminating null byte should always be present in the input buffer. */
if (c != 1)
return -3; /* Invalid framing. The skip counter does not hit the end of the frame. */
//@ assert 0 < p <= srclen <= 65535;
//@ assert src[p] == 0;
//@ assert \forall integer i; 1 <= i < p ==> src[i] != 0;
return p-1;
}
void cobs_decode_incremental_initialize(struct cobs_decode_state *state) {
state->p = 0;
state->c = 0;
}
int cobs_decode_incremental(struct cobs_decode_state *state, char *dst, size_t dstlen, char src) {
if (state->p == 0) {
if (src == 0)
goto empty_errout; /* invalid framing. An empty frame would be [...] 00 01 00, not [...] 00 00 */
state->c = (unsigned char)src;
state->p++;
return -1;
}
if (!src) {
if (state->c != 1)
goto errout; /* Invalid framing. The skip counter does not hit the end of the frame. */
int rv = state->p-1;
cobs_decode_incremental_initialize(state);
return rv;
}
char val;
state->c--;
if (state->c == 0) {
state->c = (unsigned char)src;
val = 0;
} else {
val = src;
}
size_t pos = state->p-1;
if (pos >= dstlen)
return -4; /* output buffer too small */
dst[pos] = val;
state->p++;
return -1;
errout:
cobs_decode_incremental_initialize(state);
return -2;
empty_errout:
cobs_decode_incremental_initialize(state);
return -3;
}
#ifdef VALIDATION
/*@
@ requires 0 <= d < 256;
@ assigns \nothing;
@*/
size_t test(char foo, unsigned int d) {
unsigned int c = (unsigned char)foo;
if (c != 0) {
//@ assert c < 256;
//@ assert c >= 0;
//@ assert c != 0;
//@ assert c > 0;
}
if (d != 0) {
//@ assert d >= 0;
//@ assert d != 0;
//@ assert d > 0;
}
return c + d;
}
#include <__fc_builtin.h>
void main(void) {
char inbuf[254];
char cobsbuf[256];
char outbuf[256];
size_t range = Frama_C_interval(0, sizeof(inbuf));
Frama_C_make_unknown((char *)inbuf, range);
cobs_encode(cobsbuf, sizeof(cobsbuf), inbuf, sizeof(inbuf));
cobs_decode(outbuf, sizeof(outbuf), cobsbuf, sizeof(cobsbuf));
//@ assert \forall integer i; 0 <= i < sizeof(inbuf) ==> outbuf[i] == inbuf[i];
}
#endif//VALIDATION

62
src/interrupts.c Normal file
View file

@ -0,0 +1,62 @@
#include "fe_global.h"
#include "fe_interrupts.h"
uint64_t sys_time_millis;
/******************************************************************************/
/* Cortex-M4 CPU Interrupts */
/******************************************************************************/
void NMI_Handler(void)
{
}
void HardFault_Handler(void)
{
while (42) {
asm volatile ("bkpt #13");
}
}
void MemManage_Handler(void)
{
while (42) {
asm volatile ("bkpt #12");
}
}
void BusFault_Handler(void)
{
while (42) {
asm volatile ("bkpt #11");
}
}
void UsageFault_Handler(void)
{
while (42) {
asm volatile ("bkpt #10");
}
}
void SVC_Handler(void)
{
}
void DebugMon_Handler(void)
{
}
void PendSV_Handler(void)
{
}
void SysTick_Handler(void)
{
sys_time_millis += 1;
}
/******************************************************************************/
/* STM32 Peripheral interrupts */
/******************************************************************************/

370
src/main.c Normal file
View file

@ -0,0 +1,370 @@
#include <fe_global.h>
#include <fe_clocks.h>
#include <bootloader.h>
#include <cobs.h>
/* User configuration */
#include <fe_config_backend.h>
struct usart_config {
uint32_t rx_rcc_ahbenr_flags;
GPIO_TypeDef *rx_gpio;
int rx_pin;
int rx_alt;
uint32_t tx_rcc_ahbenr_flags;
GPIO_TypeDef *tx_gpio;
int tx_pin;
int tx_alt;
uint32_t apb1enr_mask;
uint32_t apb2enr_mask;
USART_TypeDef *usart;
};
struct usart_config usart_gpios[FE_CONFIG_USART_COUNT] = {
[FE_CONFIG_USART1_PB7] = {FE_CONFIG_GPIOB, 7, 7, FE_CONFIG_GPIOB, 6, 7, 0, RCC_APB2ENR_USART1EN, USART1},
};
void run_bootloader(void);
int bootloader_handle_cmd(USART_TypeDef *us, char *buf, size_t len);
void usart_init(struct usart_config *uc);
void usart_putc(USART_TypeDef *us, char c);
void usart_puts(USART_TypeDef *us, const char *data);
int usart_rx(USART_TypeDef *us);
int usart_tx_framed(USART_TypeDef * us, char *buf, size_t len);
void __libc_init_array(void) { /* we don't need this. */ }
void __assert_func (unused_a const char *file, unused_a int line, unused_a const char *function, unused_a const char *expr) {
asm volatile ("bkpt #69");
while(1) {}
}
void gpio_config(GPIO_TypeDef *gpio, int pin, int mode, int speed, int pullups, int alt) {
gpio->MODER &= ~(3<<(2*pin));
gpio->MODER |= mode<<(2*pin);
gpio->OSPEEDR &= ~(3<<(2*pin));
gpio->OSPEEDR |= speed<<(2*pin);
gpio->PUPDR &= ~(3<<(2*pin));
gpio->PUPDR |= pullups<<(2*pin);
if (pin < 8) {
gpio->AFR[0] &= ~(0xf << (4*pin));
gpio->AFR[0] |= alt << (4*pin);
} else {
pin -= 8;
gpio->AFR[1] &= ~(0xf << (4*pin));
gpio->AFR[1] |= alt << (4*pin);
}
}
int main(void)
{
const struct fe_config_def *c = &fe_config;
uint32_t old_moder = c->bootloader_enable_pin.gpio->MODER;
uint32_t old_ospeedr = c->bootloader_enable_pin.gpio->OSPEEDR;
uint32_t old_pupdr = c->bootloader_enable_pin.gpio->PUPDR;
RCC->AHBENR |= c->bootloader_enable_pin.rcc_ahbenr_flags;
int pullups = c->bootloader_enable_level ? 2 : 1;
gpio_config(c->bootloader_enable_pin.gpio, c->bootloader_enable_pin.pin_number, 0, 0, pullups, 0);
delay_ms(50);
int enable_pin_state = ((c->bootloader_enable_pin.gpio->IDR >> c->bootloader_enable_pin.pin_number) & 1);
if (enable_pin_state == c->bootloader_enable_level || !fe_check_img_valid()) {
fe_config_clocks();
run_bootloader();
fe_system_reset();
} else {
c->bootloader_enable_pin.gpio->MODER = old_moder;
c->bootloader_enable_pin.gpio->OSPEEDR = old_ospeedr;
c->bootloader_enable_pin.gpio->PUPDR = old_pupdr;
fe_jump_to_application();
}
/* Should never be reached. */
assert(0);
return 0;
}
#define MAX_RX_SIZE 512
char rx_buf[MAX_RX_SIZE];
void run_bootloader() {
assert(fe_config.usart <= FE_CONFIG_USART_COUNT);
struct usart_config *uc = &usart_gpios[fe_config.usart];
usart_init(uc);
usart_puts(uc->usart, fe_config.welcome_string);
usart_putc(uc->usart, 0x00);
struct cobs_decode_state cobs_st;
cobs_decode_incremental_initialize(&cobs_st);
while (42) {
int c = usart_rx(uc->usart);
if (c == -2) {
/* ignore errors for now */
continue;
} else if (c == -1) {
/* We received nothing */
continue;
}
int rc = cobs_decode_incremental(&cobs_st, rx_buf, sizeof(rx_buf), c);
if (rc == -1) {
continue;
} else if (rc < 0) {
/* Ignore errors for now */
continue;
}
(void)bootloader_handle_cmd(uc->usart, rx_buf, rc); /* ignore errors for now */
}
}
enum bootloader_cmd {
/* Generic commands */
FE_CMD_REPLY = 0,
FE_CMD_PING = 1,
FE_CMD_IDENTIFY = 2,
/* Bootloader commands */
FE_CMD_ERASE = 16 + 0,
FE_CMD_WRITE_BLOCK = 16 + 1,
FE_CMD_REBOOT = 16 + 2,
/* Reserved commands */
FE_CMD_RESERVED0 = 'e', /* 0x65 / 101 Reserved for welcome string */
};
enum error_codes {
FE_SUCCESS = 0,
FE_ECMD = 1,
FE_ESIZE = 2,
FE_EINVAL = 3,
FE_ESYS = 4,
};
#define FE_MAX_PACKET_SIZE 254
struct cmd_header {
uint8_t cmd; /* Command code, see enum bootloader_cmd */
uint32_t tag; /* Tag for request/response identification. Echoed back to requestor in response. */
} __attribute__((packed));
#define FE_MAX_PAYLOAD_SIZE (FE_MAX_PACKET_SIZE - sizeof(struct cmd_header))
struct id_response {
uint8_t major_version;
uint8_t minor_version;
uint32_t idcode;
uint8_t serial[6];
char id_string[FE_MAX_PAYLOAD_SIZE - 2];
};
struct simple_response {
int32_t return_code;
};
struct cmd_packet {
struct cmd_header hdr;
union {
uint8_t data[FE_MAX_PAYLOAD_SIZE];
struct id_response id_response;
struct simple_response simple_response;
};
};
static const uint8_t major_version = 1;
static const uint8_t minor_version = 0;
static const char *id_string = "Fenris Bootloader";
static int err_simple(USART_TypeDef *us, struct cmd_packet *pkt, int rc);
static int err_simple(USART_TypeDef *us, struct cmd_packet *pkt, int rc) {
struct simple_response *res = &pkt->simple_response;
pkt->hdr.cmd = FE_CMD_REPLY;
/* leave tag unaffected */
res->return_code = rc;
return usart_tx_framed(us, (char *)pkt, sizeof(struct cmd_header) + sizeof(struct simple_response));
}
static size_t flash_write_addr = 0;
int bootloader_handle_cmd(USART_TypeDef *us, char *buf, size_t len) {
struct cmd_packet *pkt = (struct cmd_packet *)buf;
int rc = 0;
if (len < sizeof(struct cmd_header)) {
memset(buf, 0, sizeof(struct cmd_header));
return err_simple(us, pkt, -FE_ESIZE);
}
int payload_len = len-sizeof(struct cmd_header);
pkt->hdr.cmd = FE_CMD_REPLY;
switch (pkt->hdr.cmd) {
case FE_CMD_PING:
return usart_tx_framed(us, buf, len);
case FE_CMD_IDENTIFY: {
if (payload_len != 0)
return err_simple(us, pkt, -FE_ESIZE);
struct id_response *res = &pkt->id_response;
memcpy(res->serial, (void*)UID_BASE, 12);
res->idcode = DBGMCU->IDCODE;
res->major_version = major_version;
res->minor_version = minor_version;
strncpy(res->id_string, id_string, sizeof(res->id_string));
len = sizeof(struct cmd_header) + 2 + strlen(id_string) + 1;
if (len < FE_MAX_PACKET_SIZE)
return usart_tx_framed(us, buf, len);
return -10;
}
case FE_CMD_ERASE: {
if (payload_len != 4)
return err_simple(us, pkt, -FE_ESIZE);
if (*((uint32_t*)pkt->data) != 0x54454c44)
return err_simple(us, pkt, -FE_EINVAL);
flash_unlock();
if (erase_user_flash()) {
flash_lock();
return err_simple(us, pkt, -FE_ESYS);
}
flash_lock();
flash_write_addr = flash_base;
return err_simple(us, pkt, 0);
}
case FE_CMD_WRITE_BLOCK: {
if (payload_len == 0)
return err_simple(us, pkt, -FE_ESIZE);
if ((payload_len&1) != 0)
return err_simple(us, pkt, -FE_ESIZE);
if (flash_write_addr + payload_len == flash_size) {
/* FIXME sig check */
if (0) { /* invalid signature */
return err_simple(us, pkt, -FE_EINVAL);
}
} else if (flash_write_addr + payload_len > flash_size) {
return err_simple(us, pkt, -FE_EINVAL);
}
if (!flash_write(flash_write_addr, pkt->data, payload_len))
return err_simple(us, pkt, -FE_ESYS);
flash_write_addr += payload_len;
}
case FE_CMD_REBOOT: {
fe_system_reset();
}
default: {
return err_simple(us, pkt, -FE_ECMD);
}
}
}
void usart_init(struct usart_config *uc) {
RCC->AHBENR |= uc->rx_rcc_ahbenr_flags | uc->tx_rcc_ahbenr_flags;
gpio_config(uc->rx_gpio, uc->rx_pin, 2, 3, 1, uc->rx_alt);
gpio_config(uc->tx_gpio, uc->tx_pin, 2, 3, 0, uc->tx_alt);
RCC->APB1ENR |= uc->apb1enr_mask;
RCC->APB2ENR |= uc->apb2enr_mask;
uc->usart->CR1 = USART_CR1_TE | USART_CR1_RE;
int bus_speed = uc->apb1enr_mask ? apb1_speed : apb2_speed;
uc->usart->BRR = bus_speed * 16 / fe_config.baudrate / 16;
uc->usart->CR1 |= USART_CR1_UE;
}
void usart_putc(USART_TypeDef *us, char c) {
while (!(us->ISR & USART_ISR_TXE))
;
us->TDR = c;
}
void usart_puts(USART_TypeDef *us, const char *data) {
for (const char *c = data; *c; c++)
usart_putc(us, *c);
}
int usart_rx(USART_TypeDef *us) {
if ((us->ISR & USART_ISR_ORE) || (us->ISR & USART_ISR_PE)) {
/* Ignore overruns or parity errors */
us->ICR = USART_ICR_ORECF | USART_ICR_PECF;
return -2;
}
if (us->ISR & USART_ISR_RXNE)
return us->RDR;
return -1;
}
static int cobs_encode_usart_output(void *userdata, char c);
static int cobs_encode_usart_output(void *userdata, char c) {
usart_putc((USART_TypeDef *)userdata, c);
return 0;
}
int usart_tx_framed(USART_TypeDef * us, char *buf, size_t len) {
return cobs_encode_usart(cobs_encode_usart_output, us, buf, len);
}
void *memcpy(void *restrict dest, const void *restrict src, size_t n)
{
unsigned char *d = dest;
const unsigned char *s = src;
for (; n; n--) *d++ = *s++;
return dest;
}
void *memset(void *dest, int c, size_t n)
{
unsigned char *s = dest;
size_t k;
/* Fill head and tail with minimal branching. Each
* conditional ensures that all the subsequently used
* offsets are well-defined and in the dest region. */
if (!n) return dest;
s[0] = c;
s[n-1] = c;
if (n <= 2) return dest;
s[1] = c;
s[2] = c;
s[n-2] = c;
s[n-3] = c;
if (n <= 6) return dest;
s[3] = c;
s[n-4] = c;
if (n <= 8) return dest;
/* Advance pointer to align it at a 4-byte boundary,
* and truncate n to a multiple of 4. The previous code
* already took care of any head/tail that get cut off
* by the alignment. */
k = -(uintptr_t)s & 3;
s += k;
n -= k;
n &= -4;
/* Pure C fallback with no aliasing violations. */
for (; n; n--, s++) *s = c;
return dest;
}

298
src/system_stm32f3xx.c Normal file
View file

@ -0,0 +1,298 @@
/**
******************************************************************************
* @file system_stm32f3xx.c
* @author MCD Application Team
* @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File.
*
* 1. This file provides two functions and one global variable to be called from
* user application:
* - SystemInit(): This function is called at startup just after reset and
* before branch to main program. This call is made inside
* the "startup_stm32f3xx.s" file.
*
* - SystemCoreClock variable: Contains the core clock (HCLK), it can be used
* by the user application to setup the SysTick
* timer or configure other parameters.
*
* - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must
* be called whenever the core clock is changed
* during program execution.
*
* 2. After each device reset the HSI (8 MHz) is used as system clock source.
* Then SystemInit() function is called, in "startup_stm32f3xx.s" file, to
* configure the system clock before to branch to main program.
*
* 3. This file configures the system clock as follows:
*=============================================================================
* Supported STM32F3xx device
*-----------------------------------------------------------------------------
* System Clock source | HSI
*-----------------------------------------------------------------------------
* SYSCLK(Hz) | 8000000
*-----------------------------------------------------------------------------
* HCLK(Hz) | 8000000
*-----------------------------------------------------------------------------
* AHB Prescaler | 1
*-----------------------------------------------------------------------------
* APB2 Prescaler | 1
*-----------------------------------------------------------------------------
* APB1 Prescaler | 1
*-----------------------------------------------------------------------------
* USB Clock | DISABLE
*-----------------------------------------------------------------------------
*=============================================================================
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
/** @addtogroup CMSIS
* @{
*/
/** @addtogroup stm32f3xx_system
* @{
*/
/** @addtogroup STM32F3xx_System_Private_Includes
* @{
*/
#include "stm32f3xx.h"
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_TypesDefinitions
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_Defines
* @{
*/
#if !defined (HSE_VALUE)
#define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSE_VALUE */
#if !defined (HSI_VALUE)
#define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz.
This value can be provided and adapted by the user application. */
#endif /* HSI_VALUE */
/*!< Uncomment the following line if you need to relocate your vector Table in
Internal SRAM. */
/* #define VECT_TAB_SRAM */
#define VECT_TAB_OFFSET 0x0 /*!< Vector Table base offset field.
This value must be a multiple of 0x200. */
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_Macros
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_Variables
* @{
*/
/* This variable is updated in three ways:
1) by calling CMSIS function SystemCoreClockUpdate()
2) by calling HAL API function HAL_RCC_GetHCLKFreq()
3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency
Note: If you use this function to configure the system clock there is no need to
call the 2 first functions listed above, since SystemCoreClock variable is
updated automatically.
*/
uint32_t SystemCoreClock = 8000000;
const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9};
const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4};
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_FunctionPrototypes
* @{
*/
/**
* @}
*/
/** @addtogroup STM32F3xx_System_Private_Functions
* @{
*/
/**
* @brief Setup the microcontroller system
* Initialize the FPU setting, vector table location and the PLL configuration is reset.
* @param None
* @retval None
*/
void SystemInit(void)
{
/* FPU settings ------------------------------------------------------------*/
#if (__FPU_PRESENT == 1) && (__FPU_USED == 1)
SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */
#endif
/* Reset the RCC clock configuration to the default reset state ------------*/
/* Set HSION bit */
RCC->CR |= (uint32_t)0x00000001;
/* Reset CFGR register */
RCC->CFGR &= 0xF87FC00C;
/* Reset HSEON, CSSON and PLLON bits */
RCC->CR &= (uint32_t)0xFEF6FFFF;
/* Reset HSEBYP bit */
RCC->CR &= (uint32_t)0xFFFBFFFF;
/* Reset PLLSRC, PLLXTPRE, PLLMUL and USBPRE bits */
RCC->CFGR &= (uint32_t)0xFF80FFFF;
/* Reset PREDIV1[3:0] bits */
RCC->CFGR2 &= (uint32_t)0xFFFFFFF0;
/* Reset USARTSW[1:0], I2CSW and TIMs bits */
RCC->CFGR3 &= (uint32_t)0xFF00FCCC;
/* Disable all interrupts */
RCC->CIR = 0x00000000;
#ifdef VECT_TAB_SRAM
SCB->VTOR = SRAM_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */
#else
SCB->VTOR = FLASH_BASE | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal FLASH */
#endif
}
/**
* @brief Update SystemCoreClock variable according to Clock Register Values.
* The SystemCoreClock variable contains the core clock (HCLK), it can
* be used by the user application to setup the SysTick timer or configure
* other parameters.
*
* @note Each time the core clock (HCLK) changes, this function must be called
* to update SystemCoreClock variable value. Otherwise, any configuration
* based on this variable will be incorrect.
*
* @note - The system frequency computed by this function is not the real
* frequency in the chip. It is calculated based on the predefined
* constant and the selected clock source:
*
* - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*)
*
* - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**)
*
* - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**)
* or HSI_VALUE(*) multiplied/divided by the PLL factors.
*
* (*) HSI_VALUE is a constant defined in stm32f3xx_hal.h file (default value
* 8 MHz) but the real value may vary depending on the variations
* in voltage and temperature.
*
* (**) HSE_VALUE is a constant defined in stm32f3xx_hal.h file (default value
* 8 MHz), user has to ensure that HSE_VALUE is same as the real
* frequency of the crystal used. Otherwise, this function may
* have wrong result.
*
* - The result of this function could be not correct when using fractional
* value for HSE crystal.
*
* @param None
* @retval None
*/
void SystemCoreClockUpdate (void)
{
uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0;
/* Get SYSCLK source -------------------------------------------------------*/
tmp = RCC->CFGR & RCC_CFGR_SWS;
switch (tmp)
{
case RCC_CFGR_SWS_HSI: /* HSI used as system clock */
SystemCoreClock = HSI_VALUE;
break;
case RCC_CFGR_SWS_HSE: /* HSE used as system clock */
SystemCoreClock = HSE_VALUE;
break;
case RCC_CFGR_SWS_PLL: /* PLL used as system clock */
/* Get PLL clock source and multiplication factor ----------------------*/
pllmull = RCC->CFGR & RCC_CFGR_PLLMUL;
pllsource = RCC->CFGR & RCC_CFGR_PLLSRC;
pllmull = ( pllmull >> 18) + 2;
#if defined (STM32F302xE) || defined (STM32F303xE) || defined (STM32F398xx)
predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1;
if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV)
{
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull;
}
else
{
/* HSI oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSI_VALUE / predivfactor) * pllmull;
}
#else
if (pllsource == RCC_CFGR_PLLSRC_HSI_DIV2)
{
/* HSI oscillator clock divided by 2 selected as PLL clock entry */
SystemCoreClock = (HSI_VALUE >> 1) * pllmull;
}
else
{
predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1;
/* HSE oscillator clock selected as PREDIV1 clock entry */
SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull;
}
#endif /* STM32F302xE || STM32F303xE || STM32F398xx */
break;
default: /* HSI used as system clock */
SystemCoreClock = HSI_VALUE;
break;
}
/* Compute HCLK clock frequency ----------------*/
/* Get HCLK prescaler */
tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)];
/* HCLK clock frequency */
SystemCoreClock >>= tmp;
}
/**
* @}
*/
/**
* @}
*/
/**
* @}
*/
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

414
startup_stm32f302x8.s Normal file
View file

@ -0,0 +1,414 @@
/**
******************************************************************************
* @file startup_stm32f302x8.s
* @author MCD Application Team
* @brief STM32F302x6/STM32F302x8 devices vector table for GCC toolchain.
* This module performs:
* - Set the initial SP
* - Set the initial PC == Reset_Handler,
* - Set the vector table entries with the exceptions ISR address,
* - Configure the clock system
* - Branches to main in the C library (which eventually
* calls main()).
* After Reset the Cortex-M4 processor is in Thread mode,
* priority is Privileged, and the Stack is set to Main.
******************************************************************************
* @attention
*
* <h2><center>&copy; Copyright (c) 2016 STMicroelectronics.
* All rights reserved.</center></h2>
*
* This software component is licensed by ST under BSD 3-Clause license,
* the "License"; You may not use this file except in compliance with the
* License. You may obtain a copy of the License at:
* opensource.org/licenses/BSD-3-Clause
*
******************************************************************************
*/
.syntax unified
.cpu cortex-m4
.fpu softvfp
.thumb
.global g_pfnVectors
.global Default_Handler
/* start address for the initialization values of the .data section.
defined in linker script */
.word _sidata
/* start address for the .data section. defined in linker script */
.word _sdata
/* end address for the .data section. defined in linker script */
.word _edata
/* start address for the .bss section. defined in linker script */
.word _sbss
/* end address for the .bss section. defined in linker script */
.word _ebss
.equ BootRAM, 0xF1E0F85F
/**
* @brief This is the code that gets called when the processor first
* starts execution following a reset event. Only the absolutely
* necessary set is performed, after which the application
* supplied main() routine is called.
* @param None
* @retval : None
*/
.section .text.Reset_Handler
.weak Reset_Handler
.type Reset_Handler, %function
Reset_Handler:
ldr sp, =_estack /* Atollic update: set stack pointer */
/* Copy the data segment initializers from flash to SRAM */
movs r1, #0
b LoopCopyDataInit
CopyDataInit:
ldr r3, =_sidata
ldr r3, [r3, r1]
str r3, [r0, r1]
adds r1, r1, #4
LoopCopyDataInit:
ldr r0, =_sdata
ldr r3, =_edata
adds r2, r0, r1
cmp r2, r3
bcc CopyDataInit
ldr r2, =_sbss
b LoopFillZerobss
/* Zero fill the bss segment. */
FillZerobss:
movs r3, #0
str r3, [r2], #4
LoopFillZerobss:
ldr r3, = _ebss
cmp r2, r3
bcc FillZerobss
/* Call the clock system intitialization function.*/
bl SystemInit
/* Call static constructors */
bl __libc_init_array
/* Call the application's entry point.*/
bl main
LoopForever:
b LoopForever
.size Reset_Handler, .-Reset_Handler
/**
* @brief This is the code that gets called when the processor receives an
* unexpected interrupt. This simply enters an infinite loop, preserving
* the system state for examination by a debugger.
*
* @param None
* @retval : None
*/
.section .text.Default_Handler,"ax",%progbits
Default_Handler:
Infinite_Loop:
b Infinite_Loop
.size Default_Handler, .-Default_Handler
/******************************************************************************
*
* The minimal vector table for a Cortex-M4. Note that the proper constructs
* must be placed on this to ensure that it ends up at physical address
* 0x0000.0000.
*
******************************************************************************/
.section .isr_vector,"a",%progbits
.type g_pfnVectors, %object
.size g_pfnVectors, .-g_pfnVectors
g_pfnVectors:
.word _estack
.word Reset_Handler
.word NMI_Handler
.word HardFault_Handler
.word MemManage_Handler
.word BusFault_Handler
.word UsageFault_Handler
.word 0
.word 0
.word 0
.word 0
.word SVC_Handler
.word DebugMon_Handler
.word 0
.word PendSV_Handler
.word SysTick_Handler
.word WWDG_IRQHandler
.word PVD_IRQHandler
.word TAMP_STAMP_IRQHandler
.word RTC_WKUP_IRQHandler
.word FLASH_IRQHandler
.word RCC_IRQHandler
.word EXTI0_IRQHandler
.word EXTI1_IRQHandler
.word EXTI2_TSC_IRQHandler
.word EXTI3_IRQHandler
.word EXTI4_IRQHandler
.word DMA1_Channel1_IRQHandler
.word DMA1_Channel2_IRQHandler
.word DMA1_Channel3_IRQHandler
.word DMA1_Channel4_IRQHandler
.word DMA1_Channel5_IRQHandler
.word DMA1_Channel6_IRQHandler
.word DMA1_Channel7_IRQHandler
.word ADC1_IRQHandler
.word USB_HP_CAN_TX_IRQHandler
.word USB_LP_CAN_RX0_IRQHandler
.word CAN_RX1_IRQHandler
.word CAN_SCE_IRQHandler
.word EXTI9_5_IRQHandler
.word TIM1_BRK_TIM15_IRQHandler
.word TIM1_UP_TIM16_IRQHandler
.word TIM1_TRG_COM_TIM17_IRQHandler
.word TIM1_CC_IRQHandler
.word TIM2_IRQHandler
.word 0
.word 0
.word I2C1_EV_IRQHandler
.word I2C1_ER_IRQHandler
.word I2C2_EV_IRQHandler
.word I2C2_ER_IRQHandler
.word 0
.word SPI2_IRQHandler
.word USART1_IRQHandler
.word USART2_IRQHandler
.word USART3_IRQHandler
.word EXTI15_10_IRQHandler
.word RTC_Alarm_IRQHandler
.word USBWakeUp_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word SPI3_IRQHandler
.word 0
.word 0
.word TIM6_DAC_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word COMP2_IRQHandler
.word COMP4_6_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word 0
.word 0
.word I2C3_EV_IRQHandler
.word I2C3_ER_IRQHandler
.word USB_HP_IRQHandler
.word USB_LP_IRQHandler
.word USBWakeUp_RMP_IRQHandler
.word 0
.word 0
.word 0
.word 0
.word FPU_IRQHandler
/*******************************************************************************
*
* Provide weak aliases for each Exception handler to the Default_Handler.
* As they are weak aliases, any function with the same name will override
* this definition.
*
*******************************************************************************/
.weak NMI_Handler
.thumb_set NMI_Handler,Default_Handler
.weak HardFault_Handler
.thumb_set HardFault_Handler,Default_Handler
.weak MemManage_Handler
.thumb_set MemManage_Handler,Default_Handler
.weak BusFault_Handler
.thumb_set BusFault_Handler,Default_Handler
.weak UsageFault_Handler
.thumb_set UsageFault_Handler,Default_Handler
.weak SVC_Handler
.thumb_set SVC_Handler,Default_Handler
.weak DebugMon_Handler
.thumb_set DebugMon_Handler,Default_Handler
.weak PendSV_Handler
.thumb_set PendSV_Handler,Default_Handler
.weak SysTick_Handler
.thumb_set SysTick_Handler,Default_Handler
.weak WWDG_IRQHandler
.thumb_set WWDG_IRQHandler,Default_Handler
.weak PVD_IRQHandler
.thumb_set PVD_IRQHandler,Default_Handler
.weak TAMP_STAMP_IRQHandler
.thumb_set TAMP_STAMP_IRQHandler,Default_Handler
.weak RTC_WKUP_IRQHandler
.thumb_set RTC_WKUP_IRQHandler,Default_Handler
.weak FLASH_IRQHandler
.thumb_set FLASH_IRQHandler,Default_Handler
.weak RCC_IRQHandler
.thumb_set RCC_IRQHandler,Default_Handler
.weak EXTI0_IRQHandler
.thumb_set EXTI0_IRQHandler,Default_Handler
.weak EXTI1_IRQHandler
.thumb_set EXTI1_IRQHandler,Default_Handler
.weak EXTI2_TSC_IRQHandler
.thumb_set EXTI2_TSC_IRQHandler,Default_Handler
.weak EXTI3_IRQHandler
.thumb_set EXTI3_IRQHandler,Default_Handler
.weak EXTI4_IRQHandler
.thumb_set EXTI4_IRQHandler,Default_Handler
.weak DMA1_Channel1_IRQHandler
.thumb_set DMA1_Channel1_IRQHandler,Default_Handler
.weak DMA1_Channel2_IRQHandler
.thumb_set DMA1_Channel2_IRQHandler,Default_Handler
.weak DMA1_Channel3_IRQHandler
.thumb_set DMA1_Channel3_IRQHandler,Default_Handler
.weak DMA1_Channel4_IRQHandler
.thumb_set DMA1_Channel4_IRQHandler,Default_Handler
.weak DMA1_Channel5_IRQHandler
.thumb_set DMA1_Channel5_IRQHandler,Default_Handler
.weak DMA1_Channel6_IRQHandler
.thumb_set DMA1_Channel6_IRQHandler,Default_Handler
.weak DMA1_Channel7_IRQHandler
.thumb_set DMA1_Channel7_IRQHandler,Default_Handler
.weak ADC1_IRQHandler
.thumb_set ADC1_IRQHandler,Default_Handler
.weak USB_HP_CAN_TX_IRQHandler
.thumb_set USB_HP_CAN_TX_IRQHandler,Default_Handler
.weak USB_LP_CAN_RX0_IRQHandler
.thumb_set USB_LP_CAN_RX0_IRQHandler,Default_Handler
.weak CAN_RX1_IRQHandler
.thumb_set CAN_RX1_IRQHandler,Default_Handler
.weak CAN_SCE_IRQHandler
.thumb_set CAN_SCE_IRQHandler,Default_Handler
.weak EXTI9_5_IRQHandler
.thumb_set EXTI9_5_IRQHandler,Default_Handler
.weak TIM1_BRK_TIM15_IRQHandler
.thumb_set TIM1_BRK_TIM15_IRQHandler,Default_Handler
.weak TIM1_UP_TIM16_IRQHandler
.thumb_set TIM1_UP_TIM16_IRQHandler,Default_Handler
.weak TIM1_TRG_COM_TIM17_IRQHandler
.thumb_set TIM1_TRG_COM_TIM17_IRQHandler,Default_Handler
.weak TIM1_CC_IRQHandler
.thumb_set TIM1_CC_IRQHandler,Default_Handler
.weak TIM2_IRQHandler
.thumb_set TIM2_IRQHandler,Default_Handler
.weak I2C1_EV_IRQHandler
.thumb_set I2C1_EV_IRQHandler,Default_Handler
.weak I2C1_ER_IRQHandler
.thumb_set I2C1_ER_IRQHandler,Default_Handler
.weak I2C2_EV_IRQHandler
.thumb_set I2C2_EV_IRQHandler,Default_Handler
.weak I2C2_ER_IRQHandler
.thumb_set I2C2_ER_IRQHandler,Default_Handler
.weak SPI2_IRQHandler
.thumb_set SPI2_IRQHandler,Default_Handler
.weak USART1_IRQHandler
.thumb_set USART1_IRQHandler,Default_Handler
.weak USART2_IRQHandler
.thumb_set USART2_IRQHandler,Default_Handler
.weak USART3_IRQHandler
.thumb_set USART3_IRQHandler,Default_Handler
.weak EXTI15_10_IRQHandler
.thumb_set EXTI15_10_IRQHandler,Default_Handler
.weak RTC_Alarm_IRQHandler
.thumb_set RTC_Alarm_IRQHandler,Default_Handler
.weak USBWakeUp_IRQHandler
.thumb_set USBWakeUp_IRQHandler,Default_Handler
.weak SPI3_IRQHandler
.thumb_set SPI3_IRQHandler,Default_Handler
.weak TIM6_DAC_IRQHandler
.thumb_set TIM6_DAC_IRQHandler,Default_Handler
.weak COMP2_IRQHandler
.thumb_set COMP2_IRQHandler,Default_Handler
.weak COMP4_6_IRQHandler
.thumb_set COMP4_6_IRQHandler,Default_Handler
.weak I2C3_EV_IRQHandler
.thumb_set I2C3_EV_IRQHandler,Default_Handler
.weak I2C3_ER_IRQHandler
.thumb_set I2C3_ER_IRQHandler,Default_Handler
.weak USB_HP_IRQHandler
.thumb_set USB_HP_IRQHandler,Default_Handler
.weak USB_LP_IRQHandler
.thumb_set USB_LP_IRQHandler,Default_Handler
.weak USBWakeUp_RMP_IRQHandler
.thumb_set USBWakeUp_RMP_IRQHandler,Default_Handler
.weak FPU_IRQHandler
.thumb_set FPU_IRQHandler,Default_Handler
/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/

201
stm32f302r8tx.ld Normal file
View file

@ -0,0 +1,201 @@
/*
******************************************************************************
**
** File : LinkerScript.ld
**
** Author : Auto-generated by Ac6 System Workbench
**
** Abstract : Linker script for STM32F302R8Tx series
** 64Kbytes FLASH and 16Kbytes RAM
**
** Set heap size, stack size and stack location according
** to application requirements.
**
** Set memory bank area and size if external memory is used.
**
** Target : STMicroelectronics STM32
**
** Distribution: The file is distributed “as is,” without any warranty
** of any kind.
**
*****************************************************************************
** @attention
**
** <h2><center>&copy; COPYRIGHT(c) 2014 Ac6</center></h2>
**
** Redistribution and use in source and binary forms, with or without modification,
** are permitted provided that the following conditions are met:
** 1. Redistributions of source code must retain the above copyright notice,
** this list of conditions and the following disclaimer.
** 2. Redistributions in binary form must reproduce the above copyright notice,
** this list of conditions and the following disclaimer in the documentation
** and/or other materials provided with the distribution.
** 3. Neither the name of Ac6 nor the names of its contributors
** may be used to endorse or promote products derived from this software
** without specific prior written permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
** DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
** FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
**
*****************************************************************************
*/
/* Entry Point */
ENTRY(Reset_Handler)
/* Highest address of the user mode stack */
_estack = 0x20004000; /* end of RAM */
/* Generate a link error if heap and stack don't fit into RAM */
_Min_Heap_Size = 0x200; /* required amount of heap */
_Min_Stack_Size = 0x400; /* required amount of stack */
_Flash_Base = 0x08000000;
_Flash_Size = 64K;
_Bootloader_Size = 0x2000; /* Reserver 8k for bootloader */
/* Specify the memory areas */
MEMORY
{
FLASH (rx) : ORIGIN = _Flash_Base, LENGTH = _Bootloader_Size
USER_FLASH (xrw): ORIGIN = _Flash_Base + _Bootloader_Size, LENGTH = _Flash_Size - _Bootloader_Size
RAM (xrw) : ORIGIN = 0x20000000, LENGTH = 16K
}
/* Define output sections */
SECTIONS
{
/* The startup code goes first into FLASH */
.isr_vector :
{
. = ALIGN(4);
KEEP(*(.isr_vector)) /* Startup code */
. = ALIGN(4);
} >FLASH
/* The program code and other data goes into FLASH */
.text :
{
. = ALIGN(4);
*(.text) /* .text sections (code) */
*(.text*) /* .text* sections (code) */
*(.glue_7) /* glue arm to thumb code */
*(.glue_7t) /* glue thumb to arm code */
*(.eh_frame)
KEEP (*(.init))
KEEP (*(.fini))
. = ALIGN(4);
_etext = .; /* define a global symbols at end of code */
} >FLASH
/* Constant data goes into FLASH */
.rodata :
{
. = ALIGN(4);
*(.rodata) /* .rodata sections (constants, strings, etc.) */
*(.rodata*) /* .rodata* sections (constants, strings, etc.) */
. = ALIGN(4);
} >FLASH
.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH
.ARM : {
__exidx_start = .;
*(.ARM.exidx*)
__exidx_end = .;
} >FLASH
.preinit_array :
{
PROVIDE_HIDDEN (__preinit_array_start = .);
KEEP (*(.preinit_array*))
PROVIDE_HIDDEN (__preinit_array_end = .);
} >FLASH
.init_array :
{
PROVIDE_HIDDEN (__init_array_start = .);
KEEP (*(SORT(.init_array.*)))
KEEP (*(.init_array*))
PROVIDE_HIDDEN (__init_array_end = .);
} >FLASH
.fini_array :
{
PROVIDE_HIDDEN (__fini_array_start = .);
KEEP (*(SORT(.fini_array.*)))
KEEP (*(.fini_array*))
PROVIDE_HIDDEN (__fini_array_end = .);
} >FLASH
/* used by the startup to initialize data */
_sidata = LOADADDR(.data);
/* Initialized data sections goes into RAM, load LMA copy after code */
.data :
{
. = ALIGN(4);
_sdata = .; /* create a global symbol at data start */
*(.data) /* .data sections */
*(.data*) /* .data* sections */
. = ALIGN(4);
_edata = .; /* define a global symbol at data end */
} >RAM AT> FLASH
_user_flash_start = ORIGIN(USER_FLASH);
_user_flash_size = LENGTH(USER_FLASH);
.user_flash : {
. = ALIGN(4);
*(.user_flash)
} >USER_FLASH
/* Uninitialized data section */
. = ALIGN(4);
.bss :
{
/* This is used by the startup in order to initialize the .bss secion */
_sbss = .; /* define a global symbol at bss start */
__bss_start__ = _sbss;
*(.bss)
*(.bss*)
*(COMMON)
. = ALIGN(4);
_ebss = .; /* define a global symbol at bss end */
__bss_end__ = _ebss;
} >RAM
/* User_heap_stack section, used to check that there is enough RAM left */
._user_heap_stack :
{
. = ALIGN(8);
PROVIDE ( end = . );
PROVIDE ( _end = . );
. = . + _Min_Heap_Size;
. = . + _Min_Stack_Size;
. = ALIGN(8);
} >RAM
/* Remove information from the standard libraries */
/DISCARD/ :
{
libc.a ( * )
libm.a ( * )
libgcc.a ( * )
}
.ARM.attributes 0 : { *(.ARM.attributes) }
}

Binary file not shown.

View file

@ -0,0 +1,93 @@
#!/usr/bin/env python3
import math
import sys
import contextlib
import scipy.signal as sig
import numpy as np
@contextlib.contextmanager
def wrap(left='{', right='}', file=None, end=''):
print(left, file=file, end=end)
yield
print(right, file=file, end=end)
@contextlib.contextmanager
def print_include_guards(macro_name):
print(f'#ifndef {macro_name}')
print(f'#define {macro_name}')
print()
yield
print()
print(f'#endif /* {macro_name} */')
macro_float = lambda f: f'{f}'.replace('.', 'F').replace('-', 'N').replace('+', 'P')
ordinal = lambda n: "%d%s" % (n,"tsnrhtdd"[(n//10%10!=1)*(n%10<4)*n%10::4])
SI_TABLE = {-18: 'a', -15: 'f', -12: 'p', -9: 'n', -6: 'µ', -3: 'm', 0: '', 3: 'k', 6: 'M', 9: 'G', 12: 'T', 15: 'P', 18: 'E'}
def siprefix(x, space=' ', unit=''):
l = math.log10(x)//3*3
if l in SI_TABLE:
return f'{x/10**l}{space}{SI_TABLE[l]}{unit}'
return f'{x}{space}{unit}'
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-m', '--macro-name', default='butter_filter', help='Prefix for output macro names')
parser.add_argument('fc', type=float, help='Corner frequency [Hz]')
parser.add_argument('fs', type=float, help='Sampling rate [Hz]')
parser.add_argument('n', type=int, nargs='?', default=6, help='Filter order')
args = parser.parse_args()
sos = sig.butter(args.n, args.fc, fs=args.fs, output='sos')
print('/* THIS IS A GENERATED FILE. DO NOT EDIT! */')
print()
with print_include_guards(f'__BUTTER_FILTER_GENERATED_{args.n}_{macro_float(args.fc)}_{macro_float(args.fs)}__'):
print(f'/* {ordinal(args.n)} order Butterworth IIR filter coefficients')
print(f' *')
print(f' * corner frequency f_c = {siprefix(args.fc)}Hz')
print(f' * sampling rate f_s = {siprefix(args.fs)}Hz')
print(f' */')
print()
print(f'#define {args.macro_name.upper()}_ORDER {args.n}')
print(f'#define {args.macro_name.upper()}_CLEN {(args.n+1)//2}')
# scipy.signal.butter by default returns extremely small bs for the first biquad and large ones for subsequent
# sections. Balance magnitudes to reduce possible rounding errors.
first_biquad_bs = sos[0][:3]
approx_mag = round(math.log10(np.mean(first_biquad_bs)))
mags = [approx_mag // len(sos)] * len(sos)
mags[0] += approx_mag - sum(mags)
sos[0][:3] /= 10**approx_mag
sos = np.array([ sec * np.array([10**mag, 10**mag, 10**mag, 1, 1, 1]) for mag, sec in zip(mags, sos) ])
ones = np.ones([100000])
_, steady_state = sig.sosfilt(sos, ones, zi=np.zeros([(args.n+1)//2, 2]))
print(f'#define {args.macro_name.upper()}_COEFF ', end='')
for sec in sos:
bs, ases = sec[:3], sec[4:6]
with wrap():
print('.b=', end='')
with wrap():
print(', '.join(f'{v}' for v in bs), end='')
print(', .a=', end='')
with wrap():
print(', '.join(f'{v}' for v in ases), end='')
print(', ', end='')
print()
print(f'#define {args.macro_name.upper()}_STEADY_STATE ', end='')
for sec in steady_state:
with wrap():
print(', '.join(f'{v}' for v in sec), end='')
print(', ', end='')
print()

46
tools/crypto_test.c Normal file
View file

@ -0,0 +1,46 @@
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include "crypto.h"
void oob_trigger_activated(enum trigger_domain domain, int serial) {
printf("oob_trigger_activated(%d, %d)\n", domain, serial);
fflush(stdout);
}
void print_usage() {
fprintf(stderr, "Usage: crypto_test [auth_key_hex]\n");
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Error: Invalid arguments.\n");
print_usage();
return 1;
}
uint8_t auth_key[16];
for (size_t i=0; argv[1][i+0] != '\0' && argv[1][i+1] != '\0' && i/2<sizeof(auth_key); i+= 2) {
char buf[3] = { argv[1][i+0], argv[1][i+1], 0};
char *endptr;
auth_key[i/2] = strtoul(buf, &endptr, 16);
if (!endptr || *endptr != '\0') {
fprintf(stderr, "Invalid authkey\n");
return 1;
}
}
printf("rc=%d\n", oob_message_received(auth_key));
return 0;
}

View file

@ -0,0 +1,46 @@
#!/usr/bin/env python3
import subprocess
from os import path
import binascii
import re
import presig_gen
def do_test(domain, value, height, root_key, binary, expect_fail=False):
auth = presig_gen.gen_at_height(domain, value, height, root_key)
auth = binascii.hexlify(auth).decode()
output = subprocess.check_output([binary, auth])
*lines, rc_line = output.decode().splitlines()
rc = int(re.match('^rc=(\d+)$', rc_line).group(1))
assert expect_fail == (rc == 0)
def run_tests(root_key, max_height, binary):
for domain, value in {
'all': 'all',
'vendor': presig_gen.TEST_VENDOR,
'series': presig_gen.TEST_SERIES,
'country': presig_gen.TEST_COUNTRY,
'region': presig_gen.TEST_REGION,
}.items():
for height in range(max_height):
do_test(domain, value, height, root_key, binary)
do_test(domain, 'fail', height, root_key, binary, expect_fail=True)
do_test('fail', 'fail', height, root_key, binary, expect_fail=True)
do_test('', '', height, root_key, binary, expect_fail=True)
do_test(domain, value, max_height, root_key, binary, expect_fail=True)
do_test(domain, value, max_height+1, root_key, binary, expect_fail=True)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('keyfile', help='Root key file')
parser.add_argument('max_height', type=int, default=8, nargs='?', help='Height of generated prekeys')
default_binary = path.abspath(path.join(path.dirname(__file__), '../build/tools/crypto_test'))
parser.add_argument('binary', default=default_binary, nargs='?', help='crypto_test binary to use')
args = parser.parse_args()
with open(args.keyfile, 'r') as f:
root_key = binascii.unhexlify(f.read().strip())
run_tests(root_key, args.max_height, args.binary)

View file

@ -0,0 +1,29 @@
#!/usr/bin/env python3
import textwrap
import scipy.signal as sig
import numpy as np
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('n', type=int, help='Window size')
parser.add_argument('w', type=float, help='Wavelet width')
parser.add_argument('-v', '--variable', default='cwt_ricker_table', help='Name for alias variable pointing to generated wavelet LUT')
args = parser.parse_args()
print(f'/* CWT Ricker wavelet LUT for {args.n} sample window of width {args.w}. */')
varname = f'cwt_ricker_{args.n}_window_{str(args.w).replace(".", "F")}'
print(f'const float {varname}[{args.n}] = {{')
win = sig.ricker(args.n, args.w)
par = ' '.join(f'{f:>015.12e}f,' for f in win)
print(textwrap.fill(par,
initial_indent=' '*4, subsequent_indent=' '*4,
width=120,
replace_whitespace=False, drop_whitespace=False))
print('};')
print()
print(f'const float * const {args.variable} __attribute__((weak)) = {varname};')

109
tools/dsss_demod_test.c Normal file
View file

@ -0,0 +1,109 @@
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include "dsss_demod.h"
void handle_dsss_received(symbol_t data[static TRANSMISSION_SYMBOLS]) {
printf("data sequence received: [ ");
for (size_t i=0; i<TRANSMISSION_SYMBOLS; i++) {
//printf("%+3d", ((data[i]&1) ? 1 : -1) * (data[i]>>1));
printf("%2d", data[i]);
if (i+1 < TRANSMISSION_SYMBOLS)
printf(", ");
}
printf(" ]\n");
}
void print_usage() {
fprintf(stderr, "Usage: dsss_demod_test [test_data.bin] [optional recording channel number]\n");
}
int main(int argc, char **argv) {
if (argc != 2 && argc != 3) {
fprintf(stderr, "Error: Invalid arguments.\n");
print_usage();
return 1;
}
int fd = open(argv[1], O_RDONLY);
struct stat st;
if (fstat(fd, &st)) {
fprintf(stderr, "Error querying test data file size: %s\n", strerror(errno));
return 2;
}
if (st.st_size < 0 || st.st_size > 10000000) {
fprintf(stderr, "Error reading test data: too much test data (size=%zd)\n", st.st_size);
return 2;
}
if (st.st_size % sizeof(float) != 0) {
fprintf(stderr, "Error reading test data: file size is not divisible by %zd (size=%zd)\n", sizeof(float), st.st_size);
return 2;
}
char *buf = malloc(st.st_size);
if (!buf) {
fprintf(stderr, "Error allocating memory");
return 2;
}
int record_channel = -1;
if (argc == 3) {
char *endptr;
record_channel = strtoul(argv[2], &endptr, 10);
if (!endptr || *endptr != '\0') {
fprintf(stderr, "Invalid channel number \"%s\"\n", argv[2]);
return 1;
}
}
if (record_channel != -1)
fprintf(stderr, "Reading %zd samples test data...", st.st_size/sizeof(float));
ssize_t nread = 0;
while (nread < st.st_size) {
ssize_t rc = read(fd, buf + nread, st.st_size - nread);
if (rc == -EINTR || rc == -EAGAIN)
continue;
if (rc < 0) {
fprintf(stderr, "\nError reading test data: %s\n", strerror(errno));
return 2;
}
if (rc == 0) {
fprintf(stderr, "\nError reading test data: Unexpected end of file\n");
return 2;
}
nread += rc;
}
if (record_channel != -1)
fprintf(stderr, " done.\n");
const size_t n_samples = st.st_size / sizeof(float);
float *buf_f = (float *)buf;
if (record_channel != -1)
fprintf(stderr, "Starting simulation.\n");
struct dsss_demod_state demod;
dsss_demod_init(&demod);
for (size_t i=0; i<n_samples; i++) {
//fprintf(stderr, "Iteration %zd/%zd\n", i, n_samples);
dsss_demod_step(&demod, buf_f[i], i);
}
free(buf);
return 0;
}

View file

@ -0,0 +1,241 @@
#!/usr/bin/env python3
import os
import sys
from os import path
import subprocess
import json
from collections import namedtuple, defaultdict
from tqdm import tqdm
import uuid
import multiprocessing
import sqlite3
import time
from urllib.parse import urlparse
import tempfile
import itertools
import numpy as np
np.set_printoptions(linewidth=240)
from dsss_demod_test_waveform_gen import load_noise_gen, modulate as dsss_modulate
def build_test_binary(nbits, thf, decimation, symbols, cachedir):
build_id = str(uuid.uuid4())
builddir = path.join(cachedir, build_id)
os.mkdir(builddir)
cwd = path.join(path.dirname(__file__), '..')
env = os.environ.copy()
env['BUILDDIR'] = path.abspath(builddir)
env['DSSS_GOLD_CODE_NBITS'] = str(nbits)
env['DSSS_DECIMATION'] = str(decimation)
env['DSSS_THRESHOLD_FACTOR'] = str(thf)
env['DSSS_WAVELET_WIDTH'] = str(0.73 * decimation)
env['DSSS_WAVELET_LUT_SIZE'] = str(10 * decimation)
env['TRANSMISSION_SYMBOLS'] = str(symbols)
with open(path.join(builddir, 'make_stdout.txt'), 'w') as stdout,\
open(path.join(builddir, 'make_stderr.txt'), 'w') as stderr:
subprocess.run(['make', 'clean', os.path.abspath(path.join(builddir, 'tools/dsss_demod_test'))],
env=env, cwd=cwd, check=True, stdout=stdout, stderr=stderr)
return build_id
def sequence_matcher(test_data, decoded, max_shift=3):
match_result = []
for shift in range(-max_shift, max_shift):
failures = -shift if shift < 0 else 0 # we're skipping the first $shift symbols
a = test_data if shift > 0 else test_data[-shift:]
b = decoded if shift < 0 else decoded[shift:]
for i, (ref, found) in enumerate(itertools.zip_longest(a, b)):
if ref is None: # end of signal
break
if ref != found:
failures += 1
match_result.append(failures)
failures = min(match_result)
return failures/len(test_data)
ResultParams = namedtuple('ResultParams', ['nbits', 'thf', 'decimation', 'symbols', 'seed', 'amplitude', 'background'])
def run_test(seed, amplitude_spec, background, nbits, decimation, symbols, thfs, lookup_binary, cachedir):
noise_gen, noise_params = load_noise_gen(background)
test_data = np.random.RandomState(seed=seed).randint(0, 2 * (2**nbits), symbols)
signal = np.repeat(dsss_modulate(test_data, nbits) * 2.0 - 1, decimation)
# We're re-using the seed here. This is not a problem.
noise = noise_gen(seed, len(signal), *noise_params)
amplitudes = amplitude_spec[0] * 10 ** np.linspace(0, amplitude_spec[1], amplitude_spec[2])
# DEBUG
my_pid = multiprocessing.current_process().pid
wql = len(amplitudes) * len(thfs)
print(f'[{my_pid}] starting, got workqueue of length {wql}')
i = 0
# Map lsb to sign to match test program
# test_data = (test_data>>1) * (2*(test_data&1) - 1)
# END DEBUG
output = []
for amp in amplitudes:
with tempfile.NamedTemporaryFile(dir=cachedir) as f:
waveform = signal*amp + noise
f.write(waveform.astype('float32').tobytes())
f.flush()
# DEBUG
fcopy = f'/tmp/test-{path.basename(f.name)}'
import shutil
shutil.copy(f.name, fcopy)
# END DEBUG
for thf in thfs:
rpars = ResultParams(nbits, thf, decimation, symbols, seed, amp, background)
cmdline = [lookup_binary(nbits, thf, decimation, symbols), f.name]
# DEBUG
starttime = time.time()
# END DEBUG
try:
proc = subprocess.run(cmdline, stdout=subprocess.PIPE, encoding='utf-8', check=True, timeout=300)
lines = proc.stdout.splitlines()
matched = [ l.partition('[')[2].partition(']')[0]
for l in lines if l.strip().startswith('data sequence received:') ]
matched = [ [ int(elem) for elem in l.split(',') ] for l in matched ]
ser = min(sequence_matcher(test_data, match) for match in matched) if matched else None
output.append((rpars, ser))
# DEBUG
#print(f'[{my_pid}] ran {i}/{wql}: time={time.time() - starttime}\n {ser=}\n {rpars}\n {" ".join(cmdline)}\n {fcopy}', flush=True)
i += 1
# END DEBUG
except subprocess.TimeoutExpired:
output.append((rpars, None))
# DEBUG
print(f'[{my_pid}] ran {i}/{wql}: Timeout!\n {rpars}\n {" ".join(cmdline)}\n {fcopy}', flush=True)
i += 1
# END DEBUG
print(f'[{my_pid}] finished.')
return output
def parallel_generator(db, table, columns, builder, param_list, desc, context={}, params_mapper=lambda *args: args,
disable_cache=False):
with multiprocessing.Pool(multiprocessing.cpu_count()) as pool:
with db as conn:
jobs = []
for params in param_list:
found_res = conn.execute(
f'SELECT result FROM {table} WHERE ({",".join(columns)}) = ({",".join("?"*len(columns))})',
params_mapper(*params)).fetchone()
if found_res and not disable_cache:
yield params, json.loads(*found_res)
else:
jobs.append((params, pool.apply_async(builder, params, context)))
pool.close()
print('Using', len(param_list) - len(jobs), 'cached jobs', flush=True)
with tqdm(total=len(jobs), desc=desc) as tq:
for i, (params, res) in enumerate(jobs):
# DEBUG
print('Got result', i, params, res)
# END DEBUG
tq.update(1)
result = res.get()
with db as conn:
conn.execute(f'INSERT INTO {table} VALUES ({"?,"*len(params)}?,?)',
(*params_mapper(*params), json.dumps(result), timestamp()))
yield params, result
pool.join()
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-d', '--dump', help='Write results to JSON file')
parser.add_argument('-c', '--cachedir', default='dsss_test_cache', help='Directory to store build output and data in')
parser.add_argument('-n', '--no-cache', action='store_true', help='Disable result cache')
parser.add_argument('-b', '--batches', type=int, default=1, help='Number of batches to split the computation into')
parser.add_argument('-i', '--index', type=int, default=0, help='Batch index to compute')
parser.add_argument('-p', '--prepare', action='store_true', help='Prepare mode: compile runners, then exit.')
args = parser.parse_args()
DecoderParams = namedtuple('DecoderParams', ['nbits', 'thf', 'decimation', 'symbols'])
# dec_paramses = [ DecoderParams(nbits=nbits, thf=thf, decimation=decimation, symbols=20)
# for nbits in [5, 6]
# for thf in [4.5, 4.0, 5.0]
# for decimation in [10, 5, 22] ]
dec_paramses = [ DecoderParams(nbits=nbits, thf=thf, decimation=decimation, symbols=100)
for nbits in [5, 6]
for thf in [3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0]
for decimation in [1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 16, 22, 30, 40, 50] ]
# dec_paramses = [ DecoderParams(nbits=nbits, thf=thf, decimation=decimation, symbols=100)
# for nbits in [5, 6, 7, 8]
# for thf in [1.5, 2.0, 2.5, 3.0, 3.5, 4.0, 4.5, 5.0, 5.5, 6.0, 6.5, 7.0, 7.5, 8.0, 8.5, 9.0, 9.5, 10.0]
# for decimation in [1, 2, 3, 4, 5, 6, 9, 10, 11, 12, 16, 22, 30, 40, 50] ]
build_cache_dir = path.join(args.cachedir, 'builds')
data_cache_dir = path.join(args.cachedir, 'data')
os.makedirs(build_cache_dir, exist_ok=True)
os.makedirs(data_cache_dir, exist_ok=True)
build_db = sqlite3.connect(path.join(args.cachedir, 'build_db.sqlite3'))
build_db.execute('CREATE TABLE IF NOT EXISTS builds (nbits, thf, decimation, symbols, result, timestamp)')
timestamp = lambda: int(time.time()*1000)
builds = dict(parallel_generator(build_db, table='builds', columns=['nbits', 'thf', 'decimation', 'symbols'],
builder=build_test_binary, param_list=dec_paramses, desc='Building decoders',
context=dict(cachedir=build_cache_dir)))
print('Done building decoders.')
if args.prepare:
sys.exit(0)
GeneratorParams = namedtuple('GeneratorParams', ['seed', 'amplitude_spec', 'background'])
gen_params = [ GeneratorParams(rep, (5e-3, 1, 5), background)
#GeneratorParams(rep, (0.05e-3, 3.5, 50), background)
for rep in range(50)
for background in ['meas://fmeas_export_ocxo_2day.bin', 'synth://grid_freq_psd_spl_108pt.json'] ]
# gen_params = [ GeneratorParams(rep, (5e-3, 1, 5), background)
# for rep in range(1)
# for background in ['meas://fmeas_export_ocxo_2day.bin'] ]
data_db = sqlite3.connect(path.join(args.cachedir, 'data_db.sqlite3'))
data_db.execute('CREATE TABLE IF NOT EXISTS waveforms'
'(seed, amplitude_spec, background, nbits, decimation, symbols, thresholds, result, timestamp)')
'SELECT FROM waveforms GROUP BY (amplitude_spec, background, nbits, decimation, symbols, thresholds, result)'
dec_param_groups = defaultdict(lambda: [])
for nbits, thf, decimation, symbols in dec_paramses:
dec_param_groups[(nbits, decimation, symbols)].append(thf)
waveform_params = [ (*gp, *dp, thfs) for gp in gen_params for dp, thfs in dec_param_groups.items() ]
print(f'Generated {len(waveform_params)} parameter sets')
# Separate out our batch
waveform_params = waveform_params[args.index::args.batches]
def lookup_binary(*params):
return path.join(build_cache_dir, builds[tuple(params)], 'tools/dsss_demod_test')
def params_mapper(seed, amplitude_spec, background, nbits, decimation, symbols, thresholds):
amplitude_spec = ','.join(str(x) for x in amplitude_spec)
thresholds = ','.join(str(x) for x in thresholds)
return seed, amplitude_spec, background, nbits, decimation, symbols, thresholds
results = []
for _params, chunk in parallel_generator(data_db, 'waveforms',
['seed', 'amplitude_spec', 'background', 'nbits', 'decimation', 'symbols', 'thresholds'],
params_mapper=params_mapper,
builder=run_test,
param_list=waveform_params, desc='Simulating demodulation',
context=dict(cachedir=data_cache_dir, lookup_binary=lookup_binary),
disable_cache=args.no_cache):
results += chunk
if args.dump:
with open(args.dump, 'w') as f:
json.dump(results, f)

View file

@ -0,0 +1,86 @@
from os import path
import json
import functools
import numpy as np
import numbers
import math
from scipy import signal as sig
import scipy.fftpack
sampling_rate = 10 # sp/s
# From https://github.com/mubeta06/python/blob/master/signal_processing/sp/gold.py
preferred_pairs = {5:[[2],[1,2,3]], 6:[[5],[1,4,5]], 7:[[4],[4,5,6]],
8:[[1,2,3,6,7],[1,2,7]], 9:[[5],[3,5,6]],
10:[[2,5,9],[3,4,6,8,9]], 11:[[9],[3,6,9]]}
def gen_gold(seq1, seq2):
gold = [seq1, seq2]
for shift in range(len(seq1)):
gold.append(seq1 ^ np.roll(seq2, -shift))
return gold
def gold(n):
n = int(n)
if not n in preferred_pairs:
raise KeyError('preferred pairs for %s bits unknown' % str(n))
t0, t1 = preferred_pairs[n]
(seq0, _st0), (seq1, _st1) = sig.max_len_seq(n, taps=t0), sig.max_len_seq(n, taps=t1)
return gen_gold(seq0, seq1)
def modulate(data, nbits=5):
# 0, 1 -> -1, 1
mask = np.array(gold(nbits))*2 - 1
sel = mask[data>>1]
data_lsb_centered = ((data&1)*2 - 1)
signal = (np.multiply(sel, np.tile(data_lsb_centered, (2**nbits-1, 1)).T).flatten() + 1) // 2
return np.hstack([ np.zeros(len(mask)), signal, np.zeros(len(mask)) ])
def load_noise_meas_params(capture_file):
with open(capture_file, 'rb') as f:
meas_data = np.copy(np.frombuffer(f.read(), dtype='float32'))
meas_data -= np.mean(meas_data)
return (meas_data,)
def mains_noise_measured(seed, n, meas_data):
last_valid = len(meas_data) - n
st = np.random.RandomState(seed)
start = st.randint(last_valid)
return meas_data[start:start+n] + 50.00
def load_noise_synth_params(specfile):
with open(specfile) as f:
d = json.load(f)
return {'spl_x': np.linspace(*d['x_spec']),
'spl_N': d['x_spec'][2],
'psd_spl': (d['t'], d['c'], d['k']) }
def mains_noise_synthetic(seed, n, psd_spl, spl_N, spl_x):
st = np.random.RandomState(seed)
noise = st.normal(size=spl_N) * 2
spec = scipy.fftpack.fft(noise) **2
spec *= np.exp(scipy.interpolate.splev(spl_x, psd_spl))
spec **= 1/2
renoise = scipy.fftpack.ifft(spec)
return renoise[10000:][:n] + 50.00
@functools.lru_cache()
def load_noise_gen(url):
schema, refpath = url.split('://')
if not path.isabs(refpath):
refpath = path.abspath(path.join(path.dirname(__file__), refpath))
if schema == 'meas':
return mains_noise_measured, load_noise_meas_params(refpath)
elif schema == 'synth':
return mains_noise_synthetic, load_noise_synth_params(refpath)
else:
raise ValueError('Invalid schema', schema)

111
tools/e2e_test.c Normal file
View file

@ -0,0 +1,111 @@
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include "freq_meas.h"
#include "dsss_demod.h"
typedef uint16_t adc_data_t;
void handle_dsss_received(uint8_t data[static TRANSMISSION_SYMBOLS]) {
printf("data sequence received: [ ");
for (size_t i=0; i<TRANSMISSION_SYMBOLS; i++) {
printf("%+3d", ((data[i]&1) ? 1 : -1) * (data[i]>>1));
if (i+1 < TRANSMISSION_SYMBOLS)
printf(", ");
}
printf(" ]\n");
}
void print_usage(void);
void print_usage() {
fprintf(stderr, "Usage: e2e_test [emulated_adc_data.bin]\n");
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Error: Invalid arguments.\n");
print_usage();
return 1;
}
int fd = open(argv[1], O_RDONLY);
struct stat st;
if (fstat(fd, &st)) {
fprintf(stderr, "Error querying test data file size: %s\n", strerror(errno));
return 2;
}
if (st.st_size < 0 || st.st_size > 100000000) {
fprintf(stderr, "Error reading test data: too much test data (size=%zd)\n", st.st_size);
return 2;
}
if (st.st_size % sizeof(adc_data_t) != 0) {
fprintf(stderr, "Error reading test data: file size is not divisible by %zd (size=%zd)\n", sizeof(adc_data_t), st.st_size);
return 2;
}
char *buf = malloc(st.st_size);
if (!buf) {
fprintf(stderr, "Error allocating memory");
return 2;
}
const size_t n_samples = st.st_size / sizeof(adc_data_t);
fprintf(stderr, "Reading %zd samples test data...", n_samples);
ssize_t nread = 0;
while (nread < st.st_size) {
ssize_t rc = read(fd, buf + nread, st.st_size - nread);
if (rc == -EINTR || rc == -EAGAIN)
continue;
if (rc < 0) {
fprintf(stderr, "\nError reading test data: %s\n", strerror(errno));
return 2;
}
if (rc == 0) {
fprintf(stderr, "\nError reading test data: Unexpected end of file\n");
return 2;
}
nread += rc;
}
fprintf(stderr, " done. Read %zd bytes.\n", nread);
adc_data_t *buf_d = (adc_data_t *)buf;
struct dsss_demod_state demod;
dsss_demod_init(&demod);
fprintf(stderr, "Starting simulation.\n");
size_t iterations = (n_samples-FMEAS_FFT_LEN)/(FMEAS_FFT_LEN/2);
for (size_t i=0; i<iterations; i++) {
/*
fprintf(stderr, "Iteration %zd/%zd\n", i, iterations);
*/
float res = NAN;
int rc = adc_buf_measure_freq(buf_d + i*(FMEAS_FFT_LEN/2), &res);
if (rc)
printf("ERROR: Simulation error in iteration %zd at position %zd: %d\n", i, i*(FMEAS_FFT_LEN/2), rc);
dsss_demod_step(&demod, res, i);
/*
printf("%09zd %12f\n", i, res);
*/
}
free(buf);
return 0;
}

View file

@ -0,0 +1,59 @@
#!/usr/bin/env python3
import textwrap
import scipy.signal as sig
import numpy as np
WINDOW_TYPES = [
'boxcar',
'triang',
'blackman',
'hamming',
'hann',
'bartlett',
'flattop',
'parzen',
'bohman',
'blackmanharris',
'nuttall',
'barthann',
'kaiser',
'gaussian',
'general_gaussian',
'slepian',
'dpss',
'chebwin',
'exponential',
'tukey',
]
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('window', choices=WINDOW_TYPES, help='Type of window function to use')
parser.add_argument('n', type=int, help='Width of window in samples')
parser.add_argument('window_args', nargs='*', type=float,
help='''Window argument(s) if required. See https://docs.scipy.org/doc/scipy/reference/generated/scipy.signal.get_window.html#scipy.signal.get_window for details.''')
parser.add_argument('-v', '--variable', default='fft_window_table', help='Name for alias variable pointing to generated window')
args = parser.parse_args()
print(f'/* FTT window table for {args.n} sample {args.window} window.')
if args.window_args:
print(f' * Window arguments were: ({" ,".join(str(arg) for arg in args.window_args)})')
print(f' */')
winargs = ''.join(f'_{arg:.4g}'.replace('.', 'F') for arg in args.window_args)
varname = f'fft_{args.n}_window_{args.window}{winargs}'
print(f'const float {varname}[{args.n}] = {{')
win = sig.get_window(args.window if not args.window_args else (args.window, *args.window_args),
Nx=args.n, fftbins=True)
par = ' '.join(f'{f:>013.8g},' for f in win)
print(textwrap.fill(par,
initial_indent=' '*4, subsequent_indent=' '*4,
width=120,
replace_whitespace=False, drop_whitespace=False))
print('};')
print()
print(f'const float * const {args.variable} __attribute__((weak)) = {varname};')

Binary file not shown.

106
tools/freq_meas_test.c Normal file
View file

@ -0,0 +1,106 @@
#include <stdint.h>
#include <math.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/fcntl.h>
#include "freq_meas.h"
void print_usage(void);
void print_usage() {
fprintf(stderr, "Usage: freq_meas_test [test_data.bin]\n");
}
int main(int argc, char **argv) {
if (argc != 2) {
fprintf(stderr, "Error: Invalid arguments.\n");
print_usage();
return 1;
}
int fd = open(argv[1], O_RDONLY);
struct stat st;
if (fstat(fd, &st)) {
fprintf(stderr, "Error querying test data file size: %s\n", strerror(errno));
return 2;
}
if (st.st_size < 0 || st.st_size > 1000000) {
fprintf(stderr, "Error reading test data: too much test data (size=%zd)\n", st.st_size);
return 2;
}
if (st.st_size % sizeof(float) != 0) {
fprintf(stderr, "Error reading test data: file size is not divisible by %zd (size=%zd)\n", sizeof(float), st.st_size);
return 2;
}
char *buf = malloc(st.st_size);
if (!buf) {
fprintf(stderr, "Error allocating memory");
return 2;
}
fprintf(stderr, "Reading %zd samples test data...", st.st_size/sizeof(float));
ssize_t nread = 0;
while (nread < st.st_size) {
ssize_t rc = read(fd, buf + nread, st.st_size - nread);
if (rc == -EINTR || rc == -EAGAIN)
continue;
if (rc < 0) {
fprintf(stderr, "\nError reading test data: %s\n", strerror(errno));
return 2;
}
if (rc == 0) {
fprintf(stderr, "\nError reading test data: Unexpected end of file\n");
return 2;
}
nread += rc;
}
fprintf(stderr, " done.\n");
const size_t n_samples = st.st_size / sizeof(float);
float *buf_f = (float *)buf;
int16_t *sim_adc_buf = calloc(sizeof(int16_t), n_samples);
if (!sim_adc_buf) {
fprintf(stderr, "Error allocating memory\n");
return 2;
}
fprintf(stderr, "Converting and truncating test data...");
for (size_t i=0; i<n_samples; i++)
/* Note on scaling: We can't simply scale by 0x8000 (1/2 full range) here. Our test data is nominally 1Vp-p but
* certain tests such as the interharmonics one can have some samples exceeding that range. */
sim_adc_buf[i] = buf_f[i] * (0x4000-1);
fprintf(stderr, " done.\n");
fprintf(stderr, "Starting simulation.\n");
size_t iterations = (n_samples-FMEAS_FFT_LEN)/(FMEAS_FFT_LEN/2);
for (size_t i=0; i<iterations; i++) {
fprintf(stderr, "Iteration %zd/%zd\n", i, iterations);
float res = NAN;
int rc = adc_buf_measure_freq(sim_adc_buf + i*(FMEAS_FFT_LEN/2), &res);
if (rc)
printf("ERROR: Simulation error in iteration %zd at position %zd: %d\n", i, i*(FMEAS_FFT_LEN/2), rc);
printf("%09zd %12f\n", i, res);
}
free(buf);
free(sim_adc_buf);
return 0;
}

View file

@ -0,0 +1,39 @@
#!/usr/bin/env python3
import os
from os import path
import subprocess
import json
import numpy as np
np.set_printoptions(linewidth=240)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument(metavar='test_data_directory', dest='dir', help='Directory with test data .bin files')
default_binary = path.abspath(path.join(path.dirname(__file__), '../build/tools/freq_meas_test'))
parser.add_argument(metavar='test_binary', dest='binary', nargs='?', default=default_binary)
parser.add_argument('-d', '--dump', help='Write raw measurements to JSON file')
args = parser.parse_args()
bin_files = [ path.join(args.dir, d) for d in os.listdir(args.dir) if d.lower().endswith('.bin') ]
savedata = {}
for p in bin_files:
output = subprocess.check_output([args.binary, p], stderr=subprocess.DEVNULL)
measurements = np.array([ float(value) for _offset, value in [ line.split() for line in output.splitlines() ] ])
savedata[p] = list(measurements)
# Cut off first and last sample for mean and RMS calculations as these show boundary effects.
measurements = measurements[1:-1]
mean = np.mean(measurements)
rms = np.sqrt(np.mean(np.square(measurements - mean)))
print(f'{path.basename(p):<60}: mean={mean:<8.4f}Hz rms={rms*1000:.3f}mHz')
if args.dump:
with open(args.dump, 'w') as f:
json.dump(savedata, f)

View file

@ -0,0 +1,70 @@
#!/usr/bin/env python3
import sys
import math
import textwrap
import contextlib
import numpy as np
import scipy.signal as sig
# From https://github.com/mubeta06/python/blob/master/signal_processing/sp/gold.py
preferred_pairs = {5:[[2],[1,2,3]], 6:[[5],[1,4,5]], 7:[[4],[4,5,6]],
8:[[1,2,3,6,7],[1,2,7]], 9:[[5],[3,5,6]],
10:[[2,5,9],[3,4,6,8,9]], 11:[[9],[3,6,9]]}
def gen_gold(seq1, seq2):
gold = [seq1, seq2]
for shift in range(len(seq1)):
gold.append(seq1 ^ np.roll(seq2, -shift))
return gold
def gold(n):
n = int(n)
if not n in preferred_pairs:
raise KeyError('preferred pairs for %s bits unknown' % str(n))
t0, t1 = preferred_pairs[n]
(seq0, _st0), (seq1, _st1) = sig.max_len_seq(n, taps=t0), sig.max_len_seq(n, taps=t1)
return gen_gold(seq0, seq1)
@contextlib.contextmanager
def print_include_guards(macro_name):
print(f'#ifndef {macro_name}')
print(f'#define {macro_name}')
yield
print(f'#endif /* {macro_name} */')
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(add_help=False)
parser.add_argument('n', type=int, choices=preferred_pairs, help='bit width of shift register. Generate 2**n + 1 sequences of length 2**n - 1.')
parser.add_argument('-v', '--variable', default='gold_code_table', help='Name for weak alias of generated table')
parser.add_argument('-h', '--header', action='store_true', help='Generate header file')
parser.add_argument('-c', '--source', action='store_true', help='Generate table source file')
args = parser.parse_args()
if not args.header != args.source:
print('Exactly one of --header and --source must be given.', file=sys.stderr)
sys.exit(1)
nbytes = math.ceil((2**args.n-1)/8)
if args.source:
print('/* THIS IS A GENERATED FILE. DO NOT EDIT! */')
print('#include <unistd.h>')
print('#include <stdint.h>')
print()
print(f'/* {args.n} bit gold sequences: {2**args.n+1} sequences of length {2**args.n-1} bit.')
print(f' *')
print(f' * Each code is packed left-aligned into {nbytes} bytes in big-endian byte order.')
print(f' */')
print(f'const uint8_t {args.variable}[{2**args.n+1}][{nbytes}] = {{')
for i, code in enumerate(gold(args.n)):
par = '{' + ' '.join(f'0x{d:02x},' for d in np.packbits(code)) + f'}}, /* {i: 3d} "{"".join(str(x) for x in code)}" */'
print(textwrap.fill(par, initial_indent=' '*4, subsequent_indent=' '*4, width=120))
print('};')
print()
else:
print('/* THIS IS A GENERATED FILE. DO NOT EDIT! */')
with print_include_guards(f'__GOLD_CODE_GENERATED_HEADER_{args.n}__'):
print(f'extern const uint8_t {args.variable}[{2**args.n+1}][{nbytes}];')

View file

@ -0,0 +1 @@
{"x_spec": [3.2595692805152726e-05, 5.0, 613575], "t": [3.2595692805152726e-05, 3.2595692805152726e-05, 3.2595692805152726e-05, 3.2595692805152726e-05, 0.0001423024947075771, 0.00015800362803968106, 0.00017543716661470822, 0.00019479425764873777, 0.0002162871388378975, 0.00024015146540428407, 0.00026664889389955537, 0.00029606995109590574, 0.00032873721941990017, 0.0003650088738553592, 0.0004052826090950758, 0.00045000000000000004, 0.000499651343175437, 0.0005547810327489297, 0.0006159935292916862, 0.0006839599873288199, 0.0007594256141046668, 0.0008432178402871724, 0.0009362553921977272, 0.0010395583650374223, 0.0011542594075560205, 0.001281616140796111, 0.0014230249470757708, 0.001580036280396809, 0.0017543716661470824, 0.0019479425764873776, 0.002162871388378975, 0.0024015146540428403, 0.002666488938995554, 0.002960699510959057, 0.0032873721941990056, 0.0036500887385535925, 0.004052826090950754, 0.0045000000000000005, 0.00499651343175437, 0.005547810327489296, 0.006159935292916869, 0.0068395998732882, 0.007594256141046669, 0.008432178402871724, 0.009362553921977271, 0.010395583650374221, 0.011542594075560205, 0.012816161407961109, 0.014230249470757707, 0.01580036280396809, 0.017543716661470823, 0.01947942576487376, 0.02162871388378975, 0.024015146540428405, 0.026664889389955565, 0.02960699510959057, 0.03287372194199005, 0.036500887385535925, 0.04052826090950754, 0.045, 0.0499651343175437, 0.05547810327489296, 0.06159935292916863, 0.06839599873288206, 0.07594256141046668, 0.08432178402871732, 0.09362553921977272, 0.10395583650374222, 0.11542594075560206, 0.12816161407961107, 0.14230249470757705, 0.15800362803968088, 0.1754371666147082, 0.1947942576487376, 0.21628713883789774, 0.24015146540428406, 0.26664889389955565, 0.2960699510959057, 0.32873721941990053, 0.36500887385535924, 0.40528260909507535, 0.45, 0.499651343175437, 0.5547810327489296, 0.6159935292916868, 0.6839599873288206, 0.7594256141046669, 0.8432178402871732, 0.9362553921977271, 1.0395583650374223, 1.1542594075560206, 1.2816161407961109, 1.4230249470757708, 1.5800362803968104, 1.7543716661470823, 1.9479425764873777, 2.162871388378975, 2.4015146540428405, 2.6664889389955535, 2.960699510959057, 3.287372194199002, 3.6500887385535927, 4.052826090950758, 4.5, 5.0, 5.0, 5.0, 5.0], "c": [0.7720161468716866, -0.5547528253056444, 0.30706059086000753, 0.19422577014134906, -1.1954636661840032, 0.9215976941641111, -0.6668136393976918, -1.341269161156733, -0.16311330594842666, -1.7639636752234251, -1.238385544822954, -0.32649555618555554, -0.03086589610280171, -2.358195657381619, -0.5759152419849985, 0.1892225800004134, -1.8122889670546236, -0.8109120798216202, -0.5500991736738969, -4.680192969256771, -2.8007700704649876, 0.16866469558571784, -1.1040811840849307, -3.0243574268705546, -4.018139927365795, -4.100581028618109, -0.556354762846191, -7.414377514669229, 1.36396325920194, -6.002559557058508, -2.2113451390305365, -4.578944771104116, -4.372644849632638, -3.945339124673235, -4.778747958903158, -2.370174137632325, -5.7372466088109295, -4.707506574819875, -4.834404729330929, -5.005244244061701, -5.82644896783577, -4.717966026411524, -6.146374820241562, -4.972788381244952, -5.854957092953355, -5.702174935205885, -6.222035857079607, -6.2128389666872, -6.212821706753751, -6.253599689326325, -6.681685577659057, -6.372364384360678, -6.771223202540934, -6.856809137231159, -6.986412256164045, -7.190466178818742, -7.577896455149433, -7.515731696006047, -7.598155006351761, -7.824526916149126, -8.141496591776512, -8.36794927682997, -8.80307396767114, -8.828816533544659, -9.357524260470413, -9.658130054343863, -10.005768472049466, -10.499801262514108, -11.028689820560558, -11.413688641742898, -11.906162042727946, -12.232342460719975, -12.438432746733596, -13.088338100203112, -12.308710772618745, -11.685074853925329, -11.397838681243094, -12.265219694936695, -13.600359694898529, -14.031425961884718, -12.236885080485473, -13.527508426900974, -13.698402018452601, -13.397911198962568, -14.144410560196603, -13.905769594095293, -14.410874830544122, -14.531727635304264, -14.59275291853806, -14.35404826562502, -14.58670053318149, -14.432515268864977, -14.363428024828353, -14.429222027493264, -14.73947634127499, -14.717315405960353, -14.678539669792505, -14.825278423641382, -14.80936417940876, -14.943375264882789, -14.680885181815674, -14.54841244844906, -14.634365225950589, -14.609444790868906, 0.0, 0.0, 0.0, 0.0], "k": 3}

111
tools/hum_generator.py Normal file
View file

@ -0,0 +1,111 @@
#!/usr/bin/env python
# coding: utf-8
import binascii
import struct
import numpy as np
import pydub
from dsss_demod_test_waveform_gen import load_noise_gen, modulate as dsss_modulate
np.set_printoptions(linewidth=240)
def generate_noisy_signal(
test_data=32,
test_nbits=5,
test_decimation=10,
test_signal_amplitude=20e-3,
noise_level=10e-3,
noise_spec='synth://grid_freq_psd_spl_108pt.json',
seed=0):
#test_data = np.random.RandomState(seed=0).randint(0, 2 * (2**test_nbits), test_duration)
#test_data = np.array([0, 1, 2, 3] * 50)
if isinstance(test_data, int):
test_data = np.array(range(test_data))
signal = np.repeat(dsss_modulate(test_data, test_nbits) * 2.0 - 1, test_decimation)
noise_gen, noise_params = load_noise_gen(noise_spec)
noise = noise_gen(seed, len(signal), **noise_params)
return np.absolute(noise + signal*test_signal_amplitude)
def write_raw_frequencies_bin(outfile, **kwargs):
with open(outfile, 'wb') as f:
for x in generate_noisy_signal(**kwargs):
f.write(struct.pack('f', x))
def synthesize_sine(freqs, freqs_sampling_rate=10.0, output_sampling_rate=44100):
duration = len(freqs) / freqs_sampling_rate # seconds
afreq_out = np.interp(np.linspace(0, duration, int(duration*output_sampling_rate)), np.linspace(0, duration, len(freqs)), freqs)
return np.sin(np.cumsum(2*np.pi * afreq_out / output_sampling_rate))
def write_flac(filename, signal, sampling_rate=44100):
signal -= np.min(signal)
signal /= np.max(signal)
signal -= 0.5
signal *= 2**16 - 1
le_bytes = signal.astype(np.int16).tobytes()
seg = pydub.AudioSegment(data=le_bytes, sample_width=2, frame_rate=sampling_rate, channels=1)
seg.export(filename, format='flac')
def write_synthetic_hum_flac(filename, output_sampling_rate=44100, freqs_sampling_rate=10.0, **kwargs):
signal = generate_noisy_signal(**kwargs)
print(signal)
write_flac(filename, synthesize_sine(signal, freqs_sampling_rate, output_sampling_rate),
sampling_rate=output_sampling_rate)
def emulate_adc_signal(adc_bits=12, adc_offset=0.4, adc_amplitude=0.25, freq_sampling_rate=10.0, output_sampling_rate=1000, **kwargs):
signal = synthesize_sine(generate_noisy_signal(), freq_sampling_rate, output_sampling_rate)
signal = signal*adc_amplitude + adc_offset
smin, smax = np.min(signal), np.max(signal)
if smin < 0.0 or smax > 1.0:
raise UserWarning('Amplitude or offset too large: Signal out of bounds with min/max [{smin}, {smax}] of ADC range')
signal *= 2**adc_bits -1
return signal
def save_adc_signal(fn, signal, dtype=np.uint16):
with open(fn, 'wb') as f:
f.write(signal.astype(dtype).tobytes())
def write_emulated_adc_signal_bin(filename, **kwargs):
save_adc_signal(filename, emulate_adc_signal(**kwargs))
def hum_cmd(args):
write_synthetic_hum_flac(args.out_flac,
output_sampling_rate=args.audio_sampling_rate,
freqs_sampling_rate=args.frequency_sampling_rate,
test_data = np.array(list(binascii.unhexlify(args.data))),
test_nbits = args.symbol_bits,
test_decimation = args.decimation,
test_signal_amplitude = args.signal_level/1e3,
noise_level = args.noise_level/1e3,
noise_spec=args.noise_spec,
seed = args.random_seed)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
cmd_parser = parser.add_subparsers(required=True)
hum_parser = cmd_parser.add_parser('hum', help='Generated artificial modulated mains hum')
# output parameters
hum_parser.add_argument('-a', '--audio-sampling-rate', type=int, default=44100)
# modulation parameters
hum_parser.add_argument('-f', '--frequency-sampling-rate', type=float, default=10.0*100/128)
hum_parser.add_argument('-b', '--symbol-bits', type=int, default=5, help='bits per symbol (excluding sign bit)')
hum_parser.add_argument('-n', '--noise-level', type=float, default=1.0, help='Scale synthetic noise level')
hum_parser.add_argument('-s', '--signal-level', type=float, default=20.0, help='Synthetic noise level in mHz')
hum_parser.add_argument('-d', '--decimation', type=int, default=10, help='DSSS modulation decimation in frequency measurement cycles')
hum_parser.add_argument('-r', '--random-seed', type=int, default=0)
hum_parser.add_argument('--noise-spec', type=str, default='synth://grid_freq_psd_spl_108pt.json')
hum_parser.add_argument('out_flac', metavar='out.flac', help='FLAC output file')
hum_parser.add_argument('data', help='modulation data hex string')
hum_parser.set_defaults(func=hum_cmd)
args = parser.parse_args()
args.func(args)

126
tools/ldparser.py Normal file
View file

@ -0,0 +1,126 @@
import sys
import pyparsing as pp
from pyparsing import pyparsing_common as ppc
LPAREN, RPAREN, LBRACE, RBRACE, LBROK, RBROK, COLON, SEMICOLON, EQUALS, COMMA = map(pp.Suppress, '(){}<>:;=,')
parse_suffix_int = lambda lit: int(lit[:-1]) * (10**(3*(1 + 'kmgtpe'.find(lit[-1].lower()))))
si_suffix = pp.oneOf('k m g t p e', caseless=True)
numeric_literal = pp.Regex('0x[0-9a-fA-F]+').setName('hex int').setParseAction(pp.tokenMap(int, 16)) \
| (pp.Regex('[0-9]+[kKmMgGtTpPeE]')).setName('size int').setParseAction(pp.tokenMap(parse_suffix_int)) \
| pp.Word(pp.nums).setName('int').setParseAction(pp.tokenMap(int))
access_def = pp.Regex('[rR]?[wW]?[xX]?').setName('access literal').setParseAction(pp.tokenMap(str.lower))
origin_expr = pp.Suppress(pp.CaselessKeyword('ORIGIN')) + EQUALS + numeric_literal
length_expr = pp.Suppress(pp.CaselessKeyword('LENGTH')) + EQUALS + numeric_literal
mem_expr = pp.Group(ppc.identifier + LPAREN + access_def + RPAREN + COLON + origin_expr + COMMA + length_expr)
mem_contents = pp.ZeroOrMore(mem_expr)
mem_toplevel = pp.CaselessKeyword("MEMORY") + pp.Group(LBRACE + pp.Optional(mem_contents, []) + RBRACE)
glob = pp.Word(pp.alphanums + '._*')
match_expr = pp.Forward()
assignment = pp.Forward()
funccall = pp.Group(pp.Word(pp.alphas + '_') + LPAREN + (assignment | numeric_literal | match_expr | glob | ppc.identifier) + RPAREN + pp.Optional(SEMICOLON))
value = numeric_literal | funccall | ppc.identifier | '.'
formula = (value + pp.oneOf('+ = * / %') + value) | value
# suppress stray semicolons
assignment << (SEMICOLON | pp.Group((ppc.identifier | '.') + EQUALS + (formula | value) + pp.Optional(SEMICOLON)))
match_expr << (glob + LPAREN + pp.OneOrMore(funccall | glob) + RPAREN)
section_contents = pp.ZeroOrMore(assignment | funccall | match_expr);
section_name = pp.Regex('\.[a-zA-Z0-9_.]+')
section_def = pp.Group(section_name + pp.Optional(numeric_literal) + COLON + LBRACE + pp.Group(section_contents) +
RBRACE + pp.Optional(RBROK + ppc.identifier + pp.Optional('AT' + RBROK + ppc.identifier)))
sec_contents = pp.ZeroOrMore(section_def | assignment)
sections_toplevel = pp.Group(pp.CaselessKeyword("SECTIONS").suppress() + LBRACE + sec_contents + RBRACE)
toplevel_elements = mem_toplevel | funccall | sections_toplevel | assignment
ldscript = pp.Group(pp.ZeroOrMore(toplevel_elements))
ldscript.ignore(pp.cppStyleComment)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('linker_script', type=argparse.FileType('r'))
args = parser.parse_args()
#print(mem_expr.parseString('FLASH (rx) : ORIGIN = 0x0800000, LENGTH = 512K', parseAll=True))
# print(ldscript.parseString('''
# /* Entry Point */
# ENTRY(Reset_Handler)
#
# /* Highest address of the user mode stack */
# _estack = 0x20020000; /* end of RAM */
# /* Generate a link error if heap and stack don't fit into RAM */
# _Min_Heap_Size = 0x200;; /* required amount of heap */
# _Min_Stack_Size = 0x400;; /* required amount of stack */
# ''', parseAll=True))
print(ldscript.parseFile(args.linker_script, parseAll=True))
#print(funccall.parseString('KEEP(*(.isr_vector))'))
#print(section_contents.parseString('''
# . = ALIGN(4);
# KEEP(*(.isr_vector)) /* Startup code */
# . = ALIGN(4);
# ''', parseAll=True))
#print(section_def.parseString('''
# .text :
# {
# . = ALIGN(4);
# *(.text) /* .text sections (code) */
# *(.text*) /* .text* sections (code) */
# *(.glue_7) /* glue arm to thumb code */
# *(.glue_7t) /* glue thumb to arm code */
# *(.eh_frame)
#
# KEEP (*(.init))
# KEEP (*(.fini))
#
# . = ALIGN(4);
# _etext = .; /* define a global symbols at end of code */
# } >FLASH
# ''', parseAll=True))
#print(section_def.parseString('.ARM.extab : { *(.ARM.extab* .gnu.linkonce.armextab.*) } >FLASH', parseAll=True))
#print(assignment.parseString('__preinit_array_start = .', parseAll=True))
#print(assignment.parseString('a = 23', parseAll=True))
#print(funccall.parseString('foo (a=23)', parseAll=True))
#print(funccall.parseString('PROVIDE_HIDDEN (__preinit_array_start = .);', parseAll=True))
#print(section_def.parseString('''
# .preinit_array :
# {
# PROVIDE_HIDDEN (__preinit_array_start = .);
# KEEP (*(.preinit_array*))
# PROVIDE_HIDDEN (__preinit_array_end = .);
# } >FLASH''', parseAll=True))
#print(match_expr.parseString('*(SORT(.init_array.*))', parseAll=True))
#print(funccall.parseString('KEEP (*(SORT(.init_array.*)))', parseAll=True))
#print(section_def.parseString('''
# .init_array :
# {
# PROVIDE_HIDDEN (__init_array_start = .);
# KEEP (*(SORT(.init_array.*)))
# KEEP (*(.init_array*))
# PROVIDE_HIDDEN (__init_array_end = .);
# } >FLASH
# ''', parseAll=True))
#print(match_expr.parseString('*(.ARM.extab* .gnu.linkonce.armextab.*)', parseAll=True))
#print(formula.parseString('. + _Min_Heap_Size', parseAll=True))
#print(assignment.parseString('. = . + _Min_Heap_Size;', parseAll=True))
#print(sections_toplevel.parseString('''
# SECTIONS
# {
# .ARMattributes : { }
# }
# ''', parseAll=True))
#sys.exit(0)

276
tools/linkmem.py Normal file
View file

@ -0,0 +1,276 @@
import tempfile
import os
from os import path
import sys
import re
import subprocess
from contextlib import contextmanager
from collections import defaultdict
import colorsys
import cxxfilt
from elftools.elf.elffile import ELFFile
from elftools.elf.enums import ENUM_ST_SHNDX
from elftools.elf.descriptions import describe_symbol_type, describe_sh_type
import libarchive
import matplotlib.cm
@contextmanager
def chdir(newdir):
old_cwd = os.getcwd()
try:
os.chdir(newdir)
yield
finally:
os.chdir(old_cwd)
def keep_last(it, first=None):
last = first
for elem in it:
yield last, elem
last = elem
def delim(start, end, it, first_only=True):
found = False
for elem in it:
if end(elem):
if first_only:
return
found = False
elif start(elem):
found = True
elif found:
yield elem
def delim_prefix(start, end, it):
yield from delim(lambda l: l.startswith(start), lambda l: end is not None and l.startswith(end), it)
def trace_source_files(linker, cmdline, trace_sections=[], total_sections=['.text', '.data', '.rodata']):
with tempfile.TemporaryDirectory() as tempdir:
out_path = path.join(tempdir, 'output.elf')
output = subprocess.check_output([linker, '-o', out_path, f'-Wl,--print-map', *cmdline])
lines = [ line.strip() for line in output.decode().splitlines() ]
# FIXME also find isr vector table references
defs = {}
objs = defaultdict(lambda: 0)
aliases = {}
sec_name = None
last_loc = None
last_sym = None
line_cont = None
for last_line, line in keep_last(delim_prefix('Linker script and memory map', 'OUTPUT', lines), first=''):
if not line or line.startswith('LOAD '):
sec_name = None
continue
# first part of continuation line
if m := re.match('^(\.[0-9a-zA-Z-_.]+)$', line):
line_cont = line
sec_name = None
continue
if line_cont:
line = line_cont + ' ' + line
line_cont = None
# -ffunction-sections/-fdata-sections section
if m := re.match('^(\.[0-9a-zA-Z-_.]+)\.([0-9a-zA-Z-_.]+)\s+(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+(\S+)$', line):
sec, sym, loc, size, obj = m.groups()
*_, sym = sym.rpartition('.')
sym = cxxfilt.demangle(sym)
size = int(size, 16)
obj = path.abspath(obj)
if sec not in total_sections:
size = 0
objs[obj] += size
defs[sym] = (sec, size, obj)
sec_name, last_loc, last_sym = sec, loc, sym
continue
# regular (no -ffunction-sections/-fdata-sections) section
if m := re.match('^(\.[0-9a-zA-Z-_]+)\s+(0x[0-9a-f]+)\s+(0x[0-9a-f]+)\s+(\S+)$', line):
sec, _loc, size, obj = m.groups()
size = int(size, 16)
obj = path.abspath(obj)
if sec in total_sections:
objs[obj] += size
sec_name = sec
last_loc, last_sym = None, None
continue
# symbol def
if m := re.match('^(0x[0-9a-f]+)\s+(\S+)$', line):
loc, sym = m.groups()
sym = cxxfilt.demangle(sym)
loc = int(loc, 16)
if sym in defs:
continue
if loc == last_loc:
assert last_sym is not None
aliases[sym] = last_sym
else:
assert sec_name
defs[sym] = (sec_name, None, obj)
last_loc, last_sym = loc, sym
continue
refs = defaultdict(lambda: set())
for sym, (sec, size, obj) in defs.items():
fn, _, member = re.match('^([^()]+)(\((.+)\))?$', obj).groups()
fn = path.abspath(fn)
if member:
subprocess.check_call(['ar', 'x', '--output', tempdir, fn, member])
fn = path.join(tempdir, member)
with open(fn, 'rb') as f:
elf = ELFFile(f)
symtab = elf.get_section_by_name('.symtab')
symtab_demangled = { cxxfilt.demangle(nsym.name).replace(' ', ''): i
for i, nsym in enumerate(symtab.iter_symbols()) }
s = set()
sec_map = { sec.name: i for i, sec in enumerate(elf.iter_sections()) }
matches = [ i for name, i in sec_map.items() if re.match(f'\.rel\..*\.{sym}', name) ]
if matches:
sec = elf.get_section(matches[0])
for reloc in sec.iter_relocations():
refsym = symtab.get_symbol(reloc['r_info_sym'])
name = refsym.name if refsym.name else elf.get_section(refsym['st_shndx']).name.split('.')[-1]
s.add(name)
refs[sym] = s
for tsec in trace_sections:
matches = [ i for name, i in sec_map.items() if name == f'.rel{tsec}' ]
s = set()
if matches:
sec = elf.get_section(matches[0])
for reloc in sec.iter_relocations():
refsym = symtab.get_symbol(reloc['r_info_sym'])
s.add(refsym.name)
refs[tsec.replace('.', '_')] |= s
return objs, aliases, defs, refs
@contextmanager
def wrap(leader='', print=print, left='{', right='}'):
print(leader, left)
yield lambda *args, **kwargs: print(' ', *args, **kwargs)
print(right)
def mangle(name):
return re.sub('[^a-zA-Z0-9_]', '_', name)
hexcolor = lambda r, g, b, *_a: f'#{int(r*255):02x}{int(g*255):02x}{int(b*255):02x}'
def vhex(val):
r,g,b,_a = matplotlib.cm.viridis(1.0-val)
fc = hexcolor(r, g, b)
h,s,v = colorsys.rgb_to_hsv(r,g,b)
cc = '#000000' if v > 0.8 else '#ffffff'
return fc, cc
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('--trace-sections', type=str, action='append', default=[])
parser.add_argument('--trim-stubs', type=str, action='append', default=[])
parser.add_argument('--highlight-subdirs', type=str, default=None)
parser.add_argument('linker_binary')
parser.add_argument('linker_args', nargs=argparse.REMAINDER)
args = parser.parse_args()
trace_sections = args.trace_sections
trace_sections_mangled = { sec.replace('.', '_') for sec in trace_sections }
objs, aliases, syms, refs = trace_source_files(args.linker_binary, args.linker_args, trace_sections)
clusters = defaultdict(lambda: [])
for sym, (sec, size, obj) in syms.items():
clusters[obj].append((sym, sec, size))
max_ssize = max(size or 0 for _sec, size, _obj in syms.values())
max_osize = max(objs.values())
subdir_prefix = path.abspath(args.highlight_subdirs) + '/' if args.highlight_subdirs else '### NO HIGHLIGHT ###'
first_comp = lambda le_path: path.dirname(le_path).partition(os.sep)[0]
subdir_colors = sorted({ first_comp(obj[len(subdir_prefix):]) for obj in objs if obj.startswith(subdir_prefix) })
subdir_colors = { path: hexcolor(*matplotlib.cm.Pastel1(i/len(subdir_colors))) for i, path in enumerate(subdir_colors) }
subdir_sizes = defaultdict(lambda: 0)
for obj, size in objs.items():
if not isinstance(size, int):
continue
if obj.startswith(subdir_prefix):
subdir_sizes[first_comp(obj[len(subdir_prefix):])] += size
else:
subdir_sizes['<others>'] += size
print('Subdir sizes:', file=sys.stderr)
for subdir, size in sorted(subdir_sizes.items(), key=lambda x: x[1]):
print(f'{subdir:>20}: {size:>6,d} B', file=sys.stderr)
def lookup_highlight(path):
if args.highlight_subdirs:
if obj.startswith(subdir_prefix):
highlight_head = first_comp(path[len(subdir_prefix):])
return subdir_colors[highlight_head], highlight_head
else:
return '#e0e0e0', None
else:
return '#ddf7f4', None
with wrap('digraph G', print) as lvl1print:
print('size="23.4,16.5!";')
print('graph [fontsize=40];')
print('node [fontsize=40];')
#print('ratio="fill";')
print('rankdir=LR;')
print('ranksep=5;')
print('nodesep=0.2;')
print()
for i, (obj, obj_syms) in enumerate(clusters.items()):
with wrap(f'subgraph cluster_{i}', lvl1print) as lvl2print:
print('style = "filled";')
highlight_color, highlight_head = lookup_highlight(obj)
print(f'bgcolor = "{highlight_color}";')
print('pencolor = none;')
fc, cc = vhex(objs[obj]/max_osize)
highlight_subdir_part = f'<font face="carlito" color="{cc}" point-size="40">{highlight_head} / </font>' if highlight_head else ''
lvl2print(f'label = <<table border="0"><tr><td border="0" cellpadding="5" bgcolor="{fc}">'
f'{highlight_subdir_part}'
f'<font face="carlito" color="{cc}"><b>{path.basename(obj)} ({objs[obj]}B)</b></font>'
f'</td></tr></table>>;')
lvl2print()
for sym, sec, size in obj_syms:
has_size = isinstance(size, int) and size > 0
size_s = f' ({size}B)' if has_size else ''
fc, cc = vhex(size/max_ssize) if has_size else ('#ffffff', '#000000')
shape = 'box' if sec == '.text' else 'oval'
lvl2print(f'{mangle(sym)}[label = "{sym}{size_s}", style="rounded,filled", shape="{shape}", fillcolor="{fc}", fontname="carlito", fontcolor="{cc}" color=none];')
lvl1print()
edges = set()
for start, ends in refs.items():
for end in ends:
end = aliases.get(end, end)
if (start in syms or start in trace_sections_mangled) and end in syms:
edges.add((start, end))
for start, end in edges:
lvl1print(f'{mangle(start)} -> {mangle(end)} [style="bold", color="#333333"];')
for sec in trace_sections:
lvl1print(f'{sec.replace(".", "_")} [label = "section {sec}", shape="box", style="filled,bold"];')

62
tools/linksize.py Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env python3
def parse_linker_script(data):
pass
def link(groups):
defined_symbols = {}
undefined_symbols = set()
for group, files in groups:
while True:
found_something = False
for fn in files:
symbols = load_symbols(fn)
for symbol in symbols:
if symbol in defined_symbols:
if not group or not found_something:
break
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('-T', '--script', type=str, help='Linker script to use')
parser.add_argument('-o', '--output', type=str, help='Output file to produce')
args, rest = parser.parse_known_intermixed_args()
print(rest)
addprefix = lambda *xs: [ prefix + opt for opt in xs for prefix in ('', '-Wl,') ]
START_GROUP = addprefix('-(', '--start-group')
END_GROUP = addprefix('-)', '--end-group')
GROUP_OPTS = [*START_GROUP, *END_GROUP]
input_files = [ arg for arg in rest if not arg.startswith('-') or arg in GROUP_OPTS ]
def input_file_iter(input_files):
group = False
files = []
for arg in input_files:
if arg in START_GROUP:
assert not group
if files:
yield False, files # nested -Wl,--start-group
group, files = True, []
elif arg in END_GROUP:
assert group # missing -Wl,--start-group
if files:
yield True, files
group, files = False, []
else:
files.append(arg)
assert not group # missing -Wl,--end-group
if files:
yield False, files

118
tools/linktracer.py Normal file
View file

@ -0,0 +1,118 @@
#!/usr/bin/env python3
import re
import subprocess
import tempfile
import pprint
ARCHIVE_RE = r'([^(]*)(\([^)]*\))?'
def trace_source_files(linker, cmdline):
with tempfile.NamedTemporaryFile() as mapfile:
output = subprocess.check_output([linker, f'-Wl,--Map={mapfile.name}', *cmdline])
# intentionally use generator here
idx = 0
lines = [ line.rstrip() for line in mapfile.read().decode().splitlines() if line.strip() ]
for idx, line in enumerate(lines[idx:], start=idx):
#print('Dropping', line)
if line == 'Linker script and memory map':
break
idx += 1
objects = []
symbols = {}
sections = {}
current_object = None
last_offset = None
last_symbol = None
cont_sec = None
cont_ind = None
current_section = None
for idx, line in enumerate(lines[idx:], start=idx):
print(f'Processing >{line}')
if line.startswith('LOAD'):
_load, obj = line.split()
objects.append(obj)
continue
if line.startswith('OUTPUT'):
break
m = re.match(r'^( ?)([^ ]+)? +(0x[0-9a-z]+) +(0x[0-9a-z]+)?(.*)?$', line)
if m is None:
m = re.match(r'^( ?)([^ ]+)?$', line)
if m:
cont_ind, cont_sec = m.groups()
else:
cont_ind, cont_sec = None, None
last_offset, last_symbol = None, None
continue
indent, sec, offx, size, sym_or_src = m.groups()
if sec is None:
sec = cont_sec
ind = cont_ind
cont_sec = None
cont_ind = None
print(f'vals: indent={indent} sec={sec} offx={offx} size={size} sym_or_src={sym_or_src}')
if not re.match('^[a-zA-Z_0-9<>():*]+$', sym_or_src):
continue
if indent == '':
print(f'Section: {sec} 0x{size:x}')
current_section = sec
sections[sec] = size
last_offset = None
last_symbol = None
continue
if offx is not None:
offx = int(offx, 16)
if size is not None:
size = int(size, 16)
if size is not None and sym_or_src is not None:
# archive/object line
archive, _member = re.match(ARCHIVE_RE, sym_or_src).groups()
current_object = archive
last_offset = offx
else:
if sym_or_src is not None:
assert size is None
if last_offset is not None:
last_size = offx - last_offset
symbols[last_symbol] = (last_size, current_section)
print(f'Symbol: {last_symbol} 0x{last_size:x} @{current_section}')
last_offset = offx
last_symbol = sym_or_src
idx += 1
for idx, line in enumerate(lines[idx:], start=idx):
if line == 'Cross Reference Table':
break
idx += 1
# map which symbol was pulled from which object in the end
used_defs = {}
for line in lines:
*left, right = line.split()
archive, _member = re.match(ARCHIVE_RE, right).groups()
if left:
used_defs[''.join(left)] = archive
#pprint.pprint(symbols)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('linker_binary')
parser.add_argument('linker_args', nargs=argparse.REMAINDER)
args = parser.parse_args()
source_files = trace_source_files(args.linker_binary, args.linker_args)

129
tools/mapparse.py Normal file
View file

@ -0,0 +1,129 @@
import re
from collections import defaultdict, namedtuple
Section = namedtuple('Section', ['name', 'offset', 'objects'])
ObjectEntry = namedtuple('ObjectEntry', ['filename', 'object', 'offset', 'size'])
FileEntry = namedtuple('FileEntry', ['section', 'object', 'offset', 'length'])
class Memory:
def __init__(self, name, origin, length, attrs=''):
self.name, self.origin, self.length, self.attrs = name, origin, length, attrs
self.sections = {}
self.files = defaultdict(lambda: [])
self.totals = defaultdict(lambda: 0)
def add_toplevel(self, name, offx, length):
self.sections[name] = Section(offx, length, [])
def add_obj(self, name, offx, length, fn, obj):
base_section, sep, subsec = name[1:].partition('.')
base_section = '.'+base_section
if base_section in self.sections:
sec = secname, secoffx, secobjs = self.sections[base_section]
secobjs.append(ObjectEntry(fn, obj, offx, length))
else:
sec = None
self.files[fn].append(FileEntry(sec, obj, offx, length))
self.totals[fn] += length
class MapFile:
def __init__(self, s):
self._lines = s.splitlines()
self.memcfg = {}
self.defaultmem = Memory('default', 0, 0xffffffffffffffff)
self._parse()
def __getitem__(self, offx_or_name):
''' Lookup a memory area by name or address '''
if offx_or_name in self.memcfg:
return self.memcfg[offx_or_name]
elif isinstance(offx_or_name, int):
for mem in self.memcfg.values():
if mem.origin <= offx_or_name < mem.origin+mem.length:
return mem
else:
return self.defaultmem
raise ValueError('Invalid argument type for indexing')
def _skip(self, regex):
matcher = re.compile(regex)
for l in self:
if matcher.match(l):
break
def __iter__(self):
while self._lines:
yield self._lines.pop(0)
def _parse(self):
self._skip('^Memory Configuration')
# Parse memory segmentation info
self._skip('^Name')
for l in self:
if not l:
break
name, origin, length, *attrs = l.split()
if not name.startswith('*'):
self.memcfg[name] = Memory(name, int(origin, 16), int(length, 16), attrs[0] if attrs else '')
# Parse section information
toplevel_m = re.compile('^(\.[a-zA-Z0-9_.]+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)')
secondlevel_m = re.compile('^ (\.[a-zA-Z0-9_.]+)\s+(0x[0-9a-fA-F]+)\s+(0x[0-9a-fA-F]+)\s+(.*)$')
secondlevel_linebreak_m = re.compile('^ (\.[a-zA-Z0-9_.]+)\n')
filelike = re.compile('^(/?[^()]*\.[a-zA-Z0-9-_]+)(\(.*\))?')
linebreak_section = None
for l in self:
# Toplevel section
match = toplevel_m.match(l)
if match:
name, offx, length = match.groups()
offx, length = int(offx, 16), int(length, 16)
self[offx].add_toplevel(name, offx, length)
match = secondlevel_linebreak_m.match(l)
if match:
linebreak_section, = match.groups()
continue
if linebreak_section:
l = ' {} {}'.format(linebreak_section, l)
linebreak_section = None
# Second-level section
match = secondlevel_m.match(l)
if match:
name, offx, length, misc = match.groups()
match = filelike.match(misc)
if match:
fn, obj = match.groups()
obj = obj.strip('()') if obj else None
offx, length = int(offx, 16), int(length, 16)
self[offx].add_obj(name, offx, length, fn, obj)
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser(description='Parser GCC map file')
parser.add_argument('mapfile', type=argparse.FileType('r'), help='The GCC .map file to parse')
parser.add_argument('-m', '--memory', type=str, help='The memory segments to print, comma-separated')
args = parser.parse_args()
mf = MapFile(args.mapfile.read())
args.mapfile.close()
mems = args.memory.split(',') if args.memory else mf.memcfg.keys()
for name in mems:
mem = mf.memcfg[name]
print('Symbols by file for memory', name)
for tot, fn in reversed(sorted( (tot, fn) for fn, tot in mem.totals.items() )):
print(' {:>8} {}'.format(tot, fn))
for length, offx, sec, obj in reversed(sorted(( (length, offx, sec, obj) for sec, obj, offx, length in
mem.files[fn] ), key=lambda e: e[0] )):
name = sec.name if sec else None
print(' {:>8} {:>#08x} {}'.format(length, offx, obj))
#print('{:>16} 0x{:016x} 0x{:016x} ({:>24}) {}'.format(name, origin, length, length, attrs))

141
tools/presig_gen.py Normal file
View file

@ -0,0 +1,141 @@
#!/usr/bin/env python3
import os
import sys
import textwrap
import uuid
import hmac
import binascii
import time
from datetime import datetime
LINKING_KEY_SIZE = 15
PRESIG_VERSION = '000.001'
DOMAINS = ['all', 'country', 'region', 'vendor', 'series']
def format_hex(data, indent=4, wrap=True):
indent = ' '*indent
par = ', '.join(f'0x{b:02x}' for b in data)
par = textwrap.fill(par, width=120,
initial_indent=indent, subsequent_indent=indent,
replace_whitespace=False, drop_whitespace=False)
if wrap:
return f'{{\n{par}\n}}'
return par
def domain_string(domain, value):
return f'smart reset domain string v{PRESIG_VERSION}: domain:{domain}={value}'
def keygen_cmd(args):
if os.path.exists(args.keyfile) and not args.force:
print("Error: keyfile already exists. We won't overwrite it. Instead please remove it manually.",
file=sys.stderr)
return 1
root_key = os.urandom(LINKING_KEY_SIZE)
with open(args.keyfile, 'wb') as f:
f.write(binascii.hexlify(root_key))
f.write(b'\n')
return 0
def gen_at_height(domain, value, height, key):
# nanananananana BLOCKCHAIN!
ds = domain_string(domain, value).encode('utf-8')
for height in range(height+1):
key = hmac.digest(key, ds, 'sha512')[:LINKING_KEY_SIZE]
return key
def auth_cmd(args):
with open(args.keyfile, 'r') as f:
root_key = binascii.unhexlify(f.read().strip())
vals = [ (domain, getattr(args, domain)) for domain in DOMAINS if getattr(args, domain) is not None ]
if not vals:
vals = [('all', 'all')]
for domain, value in vals:
auth = gen_at_height(domain, value, args.height, root_key)
print(f'{domain}="{value}" @{args.height}: {binascii.hexlify(auth).decode()}')
def prekey_cmd(args):
with open(args.keyfile, 'r') as f:
root_key = binascii.unhexlify(f.read().strip())
print('#include <stdint.h>')
print('#include <assert.h>')
print()
print('#include "crypto.h"')
print()
bundle_id = uuid.uuid4().bytes
print(f'/* bundle id {binascii.hexlify(bundle_id).decode()} */')
print(f'uint8_t presig_bundle_id[16] = {format_hex(bundle_id)};')
print()
print(f'/* generated on {datetime.now()} */')
print(f'uint64_t bundle_timestamp = {int(time.time())};')
print()
print(f'int presig_height = {args.max_height};')
print()
print('const char *presig_domain_strings[_TRIGGER_DOMAIN_COUNT] = {')
for domain in DOMAINS:
ds = domain_string(domain, getattr(args, domain))
assert '"' not in ds
print(f' [TRIGGER_DOMAIN_{domain.upper()}] = "{ds}",')
print('};')
print()
print('uint8_t presig_keys[_TRIGGER_DOMAIN_COUNT][PRESIG_MSG_LEN] = {')
for domain in DOMAINS:
key = gen_at_height(domain, getattr(args, domain), args.max_height, root_key)
print(f' [TRIGGER_DOMAIN_{domain.upper()}] = {{{format_hex(key, indent=0, wrap=False)}}},')
print('};')
print()
print('static inline void __hack_asserts_only(void) {')
print(f' static_assert(_TRIGGER_DOMAIN_COUNT == {len(DOMAINS)});')
print(f' static_assert(PRESIG_MSG_LEN == {LINKING_KEY_SIZE});')
print('}')
print()
TEST_VENDOR = 'Darthenschmidt Cyberei und Verschleierungstechnik GmbH'
TEST_SERIES = 'Frobnicator v0.23.7'
TEST_REGION = 'Neuland'
TEST_COUNTRY = 'Germany'
if __name__ == '__main__':
import argparse
parser = argparse.ArgumentParser()
parser.add_argument('keyfile', help='Key file to use')
subparsers = parser.add_subparsers(title='subcommands')
keygen_parser = subparsers.add_parser('keygen', help='Generate a new key')
keygen_parser.add_argument('-f', '--force', action='store_true', help='Force overwriting existing keyfile')
keygen_parser.set_defaults(func=keygen_cmd)
auth_parser = subparsers.add_parser('auth', help='Generate one-time authentication string')
auth_parser.add_argument('height', type=int, help='Authentication string height, counting from 0 (root key)')
auth_parser.set_defaults(func=auth_cmd)
auth_parser.add_argument('-a', '--all', action='store_const', const='all', help='Vendor name for vendor domain')
auth_parser.add_argument('-v', '--vendor', type=str, nargs='?', const=TEST_VENDOR, help='Vendor name for vendor domain')
auth_parser.add_argument('-s', '--series', type=str, nargs='?', const=TEST_SERIES, help='Series identifier for series domain')
auth_parser.add_argument('-r', '--region', type=str, nargs='?', const=TEST_REGION, help='Region name for region domain')
auth_parser.add_argument('-c', '--country', type=str, nargs='?', const=TEST_COUNTRY, help='Country name for country domain')
prekey_parser = subparsers.add_parser('prekey', help='Generate prekey data .C source code file')
prekey_parser.add_argument('-m', '--max-height', type=int, default=8, help='Height of generated prekey')
prekey_parser.add_argument('-v', '--vendor', type=str, default=TEST_VENDOR, help='Vendor name for vendor domain')
prekey_parser.add_argument('-s', '--series', type=str, default=TEST_SERIES, help='Series identifier for series domain')
prekey_parser.add_argument('-r', '--region', type=str, default=TEST_REGION, help='Region name for region domain')
prekey_parser.add_argument('-c', '--country', type=str, default=TEST_COUNTRY, help='Country name for country domain')
prekey_parser.set_defaults(func=prekey_cmd, all='all')
args = parser.parse_args()
sys.exit(args.func(args))

91
tools/reed_solomon.py Normal file
View file

@ -0,0 +1,91 @@
import os, sys
import ctypes as C
import argparse
import binascii
import numpy as np
import timeit
import statistics
lib = C.CDLL('rslib.so')
lib.rslib_encode.argtypes = [C.c_int, C.c_size_t, C.POINTER(C.c_char), C.POINTER(C.c_char)]
lib.rslib_decode.argtypes = [C.c_int, C.c_size_t, C.POINTER(C.c_char)]
lib.rslib_gexp.argtypes = [C.c_int, C.c_int]
lib.rslib_gexp.restype = C.c_int
lib.rslib_decode.restype = C.c_int
lib.rslib_npar.restype = C.c_size_t
def npar():
return lib.rslib_npar()
def encode(data: bytes, nbits=8):
out = C.create_string_buffer(len(data) + lib.rslib_npar())
lib.rslib_encode(nbits, len(data), data, out)
return out.raw
def decode(data: bytes, nbits=8):
inout = C.create_string_buffer(data)
lib.rslib_decode(nbits, len(data), inout)
return inout.raw[:-lib.rslib_npar() - 1]
def cmdline_func_test(args, print=lambda *args, **kwargs: None, benchmark=False):
st = np.random.RandomState(seed=args.seed)
lfsr = [lib.rslib_gexp(i, args.bits) for i in range(2**args.bits - 1)]
print('LFSR', len(set(lfsr)), lfsr)
assert all(0 < x < 2**args.bits for x in lfsr)
assert len(set(lfsr)) == 2**args.bits - 1
print('Seed', args.seed)
for i in range(args.repeat):
print(f'Run {i}')
test_data = bytes(st.randint(2**args.bits, size=args.message_length, dtype=np.uint8))
print(' Raw:', binascii.hexlify(test_data).decode())
encoded = encode(test_data, nbits=args.bits)
print(' Encoded:', binascii.hexlify(encoded).decode())
indices = st.permutation(len(encoded))
encoded = list(encoded)
for pos in indices[:args.errors]:
encoded[pos] = st.randint(2**args.bits)
encoded = bytes(encoded)
print(' Modified:', ''.join(f'\033[91m{b:02x}\033[0m' if pos in indices[:args.errors] else f'{b:02x}' for pos, b in enumerate(encoded)))
if benchmark:
rpt = 10000
delta = timeit.timeit('decode(encoded, nbits=args.bits)',
globals={'args': args, 'decode': decode, 'encoded': encoded},
number=rpt)/rpt
print(f'Decoding runtime: {delta*1e6:.3f}μs')
decoded = decode(encoded, nbits=args.bits)
print(' Decoded:', binascii.hexlify(decoded).decode())
print(' Delta:', binascii.hexlify(
bytes(x^y for x, y in zip(test_data, decoded))
).decode().replace('0', '.'))
assert test_data == decoded
def cmdline_func_encode(args, **kwargs):
data = np.frombuffer(binascii.unhexlify(args.hex_str), dtype=np.uint8)
# Map 8 bit input to 6 bit symbol string
data = np.packbits(np.pad(np.unpackbits(data).reshape((-1, 6)), ((0,0),(2, 0))).flatten())
encoded = encode(data.tobytes(), nbits=args.bits)
print('symbol array:', ', '.join(f'0x{x:02x}' for x in encoded))
print('hex string:', binascii.hexlify(encoded).decode())
if __name__ == '__main__':
parser = argparse.ArgumentParser()
cmd_parser = parser.add_subparsers(required=True)
test_parser = cmd_parser.add_parser('test', help='Test reed-solomon implementation')
test_parser.add_argument('-m', '--message-length', type=int, default=6, help='Test message (plaintext) length in bytes')
test_parser.add_argument('-e', '--errors', type=int, default=2, help='Number of byte errors to insert into simulation')
test_parser.add_argument('-r', '--repeat', type=int, default=1000, help='Repeat experiment -r times')
test_parser.add_argument('-b', '--bits', type=int, default=8, help='Symbol bit size')
test_parser.add_argument('-s', '--seed', type=int, default=0, help='Random seed')
test_parser.set_defaults(func=cmdline_func_test)
enc_parser = cmd_parser.add_parser('encode', help='RS-Encode given hex string')
enc_parser.set_defaults(func=cmdline_func_encode)
enc_parser.add_argument('-b', '--bits', type=int, default=8, help='Symbol bit size')
enc_parser.add_argument('hex_str', type=str, help='Input data as hex string')
args = parser.parse_args()
args.func(args)