Initial commit
This commit is contained in:
commit
e16ec19e3a
51 changed files with 4909 additions and 0 deletions
12
.gitmodules
vendored
Normal file
12
.gitmodules
vendored
Normal 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
160
Makefile
Normal 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
17
config/fe_config.c
Normal 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
22
include/bootloader.h
Normal 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
23
include/cobs.h
Normal 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
14
include/fe_clocks.h
Normal 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__ */
|
||||
40
include/fe_config_backend.h
Normal file
40
include/fe_config_backend.h
Normal 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
19
include/fe_global.h
Normal 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
14
include/fe_interrupts.h
Normal 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__ */
|
||||
23
include/musl_include_shims/bits/alltypes.h
Normal file
23
include/musl_include_shims/bits/alltypes.h
Normal 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__ */
|
||||
80
include/musl_include_shims/endian.h
Normal file
80
include/musl_include_shims/endian.h
Normal 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
|
||||
40
include/musl_include_shims/features.h
Normal file
40
include/musl_include_shims/features.h
Normal 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
|
||||
6
include/musl_include_shims/fp_arch.h
Normal file
6
include/musl_include_shims/fp_arch.h
Normal 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__ */
|
||||
270
include/musl_include_shims/libm.h
Normal file
270
include/musl_include_shims/libm.h
Normal 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
337
include/stm32_hal_conf.h
Normal file
|
|
@ -0,0 +1,337 @@
|
|||
/**
|
||||
******************************************************************************
|
||||
* @file stm32f3xx_hal_conf.h
|
||||
* @author MCD Application Team
|
||||
* @brief HAL configuration file.
|
||||
******************************************************************************
|
||||
* @attention
|
||||
*
|
||||
* <h2><center>© 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
1
lib/cmsis_core
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 96d6da4e252b06dcfdc041e7df23e86161c33007
|
||||
1
lib/cmsis_device
Submodule
1
lib/cmsis_device
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 5e4ee5ed7a7b6c85176bb70a9fd3c72d6eb99f1b
|
||||
1
lib/hal_driver
Submodule
1
lib/hal_driver
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit 1761b6207318ede021706e75aae78f452d72b6fa
|
||||
1
lib/musl
Submodule
1
lib/musl
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit a5aff1972c9e3981566414b09a28e331ccd2be5d
|
||||
99
src/bootloader.c
Normal file
99
src/bootloader.c
Normal 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
83
src/clocks.c
Normal 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
211
src/cobs.c
Normal 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
62
src/interrupts.c
Normal 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
370
src/main.c
Normal 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
298
src/system_stm32f3xx.c
Normal 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>© 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
414
startup_stm32f302x8.s
Normal 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>© 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
201
stm32f302r8tx.ld
Normal 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>© 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) }
|
||||
}
|
||||
|
||||
|
||||
BIN
tools/__pycache__/dsss_demod_test_waveform_gen.cpython-38.pyc
Normal file
BIN
tools/__pycache__/dsss_demod_test_waveform_gen.cpython-38.pyc
Normal file
Binary file not shown.
BIN
tools/__pycache__/presig_gen.cpython-38.pyc
Normal file
BIN
tools/__pycache__/presig_gen.cpython-38.pyc
Normal file
Binary file not shown.
93
tools/butter_filter_gen.py
Normal file
93
tools/butter_filter_gen.py
Normal 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
46
tools/crypto_test.c
Normal 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;
|
||||
}
|
||||
46
tools/crypto_test_runner.py
Normal file
46
tools/crypto_test_runner.py
Normal 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)
|
||||
29
tools/cwt_wavelet_header_gen.py
Normal file
29
tools/cwt_wavelet_header_gen.py
Normal 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
109
tools/dsss_demod_test.c
Normal 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;
|
||||
}
|
||||
241
tools/dsss_demod_test_runner.py
Normal file
241
tools/dsss_demod_test_runner.py
Normal 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)
|
||||
|
||||
86
tools/dsss_demod_test_waveform_gen.py
Normal file
86
tools/dsss_demod_test_waveform_gen.py
Normal 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
111
tools/e2e_test.c
Normal 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;
|
||||
}
|
||||
59
tools/fft_window_header_gen.py
Normal file
59
tools/fft_window_header_gen.py
Normal 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};')
|
||||
|
||||
BIN
tools/fmeas_export_ocxo_2day.bin
Normal file
BIN
tools/fmeas_export_ocxo_2day.bin
Normal file
Binary file not shown.
106
tools/freq_meas_test.c
Normal file
106
tools/freq_meas_test.c
Normal 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;
|
||||
}
|
||||
39
tools/freq_meas_test_runner.py
Normal file
39
tools/freq_meas_test_runner.py
Normal 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)
|
||||
|
||||
70
tools/gold_code_header_gen.py
Normal file
70
tools/gold_code_header_gen.py
Normal 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}];')
|
||||
1
tools/grid_freq_psd_spl_108pt.json
Normal file
1
tools/grid_freq_psd_spl_108pt.json
Normal 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
111
tools/hum_generator.py
Normal 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
126
tools/ldparser.py
Normal 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
276
tools/linkmem.py
Normal 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
62
tools/linksize.py
Normal 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
118
tools/linktracer.py
Normal 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
129
tools/mapparse.py
Normal 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
141
tools/presig_gen.py
Normal 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
91
tools/reed_solomon.py
Normal 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)
|
||||
|
||||
Loading…
Add table
Add a link
Reference in a new issue