From f654df0e780f6d372d400578ae04ce73306721ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Krzysztof=20Cie=C5=9Blik?= Date: Thu, 21 May 2026 18:40:02 +0200 Subject: [PATCH] Initial bare-metal foundation for nRF52840 PTT-FHSS - Fully open toolchain: arm-none-eabi-gcc inside Podman/Docker container - Platform-agnostic build via justfile (auto-detects podman vs docker) - CMake + Ninja build system with arm-none-eabi toolchain file - nRF52840 linker script and startup with full 64-entry vector table - Register access via typed bitfield unions (include/regs.h) - FHSS channel sequencer: AES-128-ECB PRNG over 40 channels (2402-2480 MHz) - Low-power sleep via SYSTEM_ON WFI + GPIOTE wakeup on button press - Vendor deps as shallow git submodules: nrfx, CMSIS_5, tiny-AES-c --- .gitmodules | 9 ++ CMakeLists.txt | 77 +++++++++++++++++ Dockerfile | 23 ++++++ cmake/arm-none-eabi.cmake | 16 ++++ include/fhss.h | 6 ++ include/power.h | 4 + include/radio.h | 7 ++ include/regs.h | 90 ++++++++++++++++++++ justfile | 52 ++++++++++++ link/nrf52840.ld | 66 +++++++++++++++ src/fhss.c | 41 ++++++++++ src/main.c | 15 ++++ src/power.c | 37 +++++++++ src/radio.c | 27 ++++++ src/startup.c | 168 ++++++++++++++++++++++++++++++++++++++ vendor/CMSIS_5 | 1 + vendor/nrfx | 1 + vendor/tiny-aes-c | 1 + 18 files changed, 641 insertions(+) create mode 100644 .gitmodules create mode 100644 CMakeLists.txt create mode 100644 Dockerfile create mode 100644 cmake/arm-none-eabi.cmake create mode 100644 include/fhss.h create mode 100644 include/power.h create mode 100644 include/radio.h create mode 100644 include/regs.h create mode 100644 justfile create mode 100644 link/nrf52840.ld create mode 100644 src/fhss.c create mode 100644 src/main.c create mode 100644 src/power.c create mode 100644 src/radio.c create mode 100644 src/startup.c create mode 160000 vendor/CMSIS_5 create mode 160000 vendor/nrfx create mode 160000 vendor/tiny-aes-c diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..534f4bd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,9 @@ +[submodule "vendor/nrfx"] + path = vendor/nrfx + url = https://github.com/NordicSemiconductor/nrfx.git +[submodule "vendor/CMSIS_5"] + path = vendor/CMSIS_5 + url = https://github.com/ARM-software/CMSIS_5.git +[submodule "vendor/tiny-aes-c"] + path = vendor/tiny-aes-c + url = https://github.com/kokke/tiny-AES-c.git diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..71f3ba1 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,77 @@ +cmake_minimum_required(VERSION 3.22) + +# must be set before project() +set(CMAKE_TOOLCHAIN_FILE "${CMAKE_SOURCE_DIR}/cmake/arm-none-eabi.cmake") + +project(ptt_fhss C ASM) + +set(CMAKE_C_STANDARD 11) +set(CMAKE_C_STANDARD_REQUIRED ON) + +# ── sources ──────────────────────────────────────────────────────────────── + +add_executable(firmware + src/startup.c + src/main.c + src/radio.c + src/fhss.c + src/power.c + vendor/tiny-aes-c/aes.c +) + +# ── include paths ────────────────────────────────────────────────────────── + +target_include_directories(firmware PRIVATE + include + vendor/nrfx/mdk # nRF52840 device headers (nrf52840.h etc.) + vendor/CMSIS_5/CMSIS/Core/Include # core_cm4.h, cmsis_gcc.h etc. + vendor/tiny-aes-c +) + +# ── preprocessor defines ─────────────────────────────────────────────────── + +target_compile_definitions(firmware PRIVATE + NRF52840_XXAA # device variant required by nrfx/mdk headers +) + +# ── compiler flags ───────────────────────────────────────────────────────── + +set(CPU_FLAGS + -mcpu=cortex-m4 + -mthumb + -mfpu=fpv4-sp-d16 + -mfloat-abi=hard +) + +target_compile_options(firmware PRIVATE + ${CPU_FLAGS} + -Os + -ffunction-sections + -fdata-sections + -fno-exceptions + -Wall + -Wextra + -Werror +) + +# ── linker ───────────────────────────────────────────────────────────────── + +target_link_options(firmware PRIVATE + ${CPU_FLAGS} + -T ${CMAKE_SOURCE_DIR}/link/nrf52840.ld + -Wl,--gc-sections + -Wl,--print-memory-usage + -nostartfiles + -specs=nano.specs + -specs=nosys.specs +) + +# ── post-build: .hex + size report ──────────────────────────────────────── + +add_custom_command(TARGET firmware POST_BUILD + COMMAND arm-none-eabi-objcopy + -O ihex $ + ${CMAKE_BINARY_DIR}/firmware.hex + COMMAND arm-none-eabi-size $ + VERBATIM +) diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..952bf20 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,23 @@ +FROM debian:bookworm-slim + +ARG GCC_VERSION=13.2.rel1 +ARG TARGETARCH=amd64 + +RUN apt-get update && apt-get install -y --no-install-recommends \ + wget xz-utils cmake ninja-build \ + && rm -rf /var/lib/apt/lists/* + +RUN case "${TARGETARCH}" in \ + amd64) _arch=x86_64 ;; \ + arm64) _arch=aarch64 ;; \ + *) echo "unsupported arch: ${TARGETARCH}" >&2 && exit 1 ;; \ + esac \ + && wget -qO /tmp/gcc.tar.xz \ + "https://developer.arm.com/-/media/Files/downloads/gnu/${GCC_VERSION}/binrel/arm-gnu-toolchain-${GCC_VERSION}-${_arch}-arm-none-eabi.tar.xz" \ + && tar -xf /tmp/gcc.tar.xz -C /opt \ + && ln -s "/opt/arm-gnu-toolchain-${GCC_VERSION}-${_arch}-arm-none-eabi" /opt/arm-toolchain \ + && rm /tmp/gcc.tar.xz + +ENV PATH="/opt/arm-toolchain/bin:${PATH}" + +WORKDIR /src diff --git a/cmake/arm-none-eabi.cmake b/cmake/arm-none-eabi.cmake new file mode 100644 index 0000000..8881fc0 --- /dev/null +++ b/cmake/arm-none-eabi.cmake @@ -0,0 +1,16 @@ +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR arm) + +set(CMAKE_C_COMPILER arm-none-eabi-gcc) +set(CMAKE_CXX_COMPILER arm-none-eabi-g++) +set(CMAKE_ASM_COMPILER arm-none-eabi-gcc) +set(CMAKE_OBJCOPY arm-none-eabi-objcopy) +set(CMAKE_SIZE arm-none-eabi-size) + +# prevent cmake from link-testing the compiler against the host +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) + +# do not search host paths for libraries or headers +set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) +set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY) +set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) diff --git a/include/fhss.h b/include/fhss.h new file mode 100644 index 0000000..fb1b95b --- /dev/null +++ b/include/fhss.h @@ -0,0 +1,6 @@ +#pragma once +#include + +void fhss_init(void); +uint8_t fhss_next_channel(void); /* next channel from AES-ECB PRNG sequence */ +void fhss_sync_tick(void); /* call every FHSS_DWELL_MS ms */ diff --git a/include/power.h b/include/power.h new file mode 100644 index 0000000..119e403 --- /dev/null +++ b/include/power.h @@ -0,0 +1,4 @@ +#pragma once + +void power_init(void); +void power_sleep_until_button(void); /* SYSTEM_ON WFI, woken by GPIOTE event */ diff --git a/include/radio.h b/include/radio.h new file mode 100644 index 0000000..ca39b34 --- /dev/null +++ b/include/radio.h @@ -0,0 +1,7 @@ +#pragma once +#include + +void radio_init(void); +void radio_set_channel(uint8_t ch); /* 0–39, maps to 2402–2480 MHz */ +void radio_tx(const uint8_t *data, uint8_t len); +void radio_tx_burst(void); /* TX with FHSS hopping */ diff --git a/include/regs.h b/include/regs.h new file mode 100644 index 0000000..779ce03 --- /dev/null +++ b/include/regs.h @@ -0,0 +1,90 @@ +#pragma once +#include + +/* + * Hardware register bitfield unions for nRF52840 peripherals. + * Layout is guaranteed correct only with arm-none-eabi-gcc (LSB-first bitfields). + * Bit ranges match nRF52840 Product Specification v1.7. + */ + +/* ── GPIOTE ──────────────────────────────────────────────────────────────── */ + +/* CONFIG[n] — channel configuration */ +typedef union { + struct { + uint32_t MODE : 2; /* [1:0] 0=Disabled 1=Event 3=Task */ + uint32_t : 6; /* [7:2] reserved */ + uint32_t PSEL : 5; /* [12:8] pin number within port */ + uint32_t PORT : 1; /* [13] 0=Port0 1=Port1 */ + uint32_t : 2; /* [15:14] reserved */ + uint32_t POLARITY : 2; /* [17:16] 0=None 1=LoToHi 2=HiToLo 3=Toggle */ + uint32_t : 2; /* [19:18] reserved */ + uint32_t OUTINIT : 1; /* [20] initial output value for Task mode */ + uint32_t : 11; /* [31:21] reserved */ + } bit; + uint32_t reg; +} gpiote_config_t; + +#define GPIOTE_MODE_DISABLED 0u +#define GPIOTE_MODE_EVENT 1u +#define GPIOTE_MODE_TASK 3u +#define GPIOTE_POL_NONE 0u +#define GPIOTE_POL_LOTOHI 1u +#define GPIOTE_POL_HITOLO 2u +#define GPIOTE_POL_TOGGLE 3u + +/* INTENSET / INTENCLR — interrupt enable */ +typedef union { + struct { + uint32_t IN0 : 1; /* [0] channel 0 input event */ + uint32_t IN1 : 1; /* [1] channel 1 input event */ + uint32_t IN2 : 1; /* [2] */ + uint32_t IN3 : 1; /* [3] */ + uint32_t IN4 : 1; /* [4] */ + uint32_t IN5 : 1; /* [5] */ + uint32_t IN6 : 1; /* [6] */ + uint32_t IN7 : 1; /* [7] */ + uint32_t : 23; /* [30:8] reserved */ + uint32_t PORT : 1; /* [31] PORT event */ + } bit; + uint32_t reg; +} gpiote_inten_t; + +/* ── RADIO ───────────────────────────────────────────────────────────────── */ + +/* FREQUENCY — radio channel */ +typedef union { + struct { + uint32_t FREQUENCY : 7; /* [6:0] offset from base frequency in MHz */ + uint32_t : 1; /* [7] reserved */ + uint32_t MAP : 1; /* [8] 0: base=2400 MHz 1: base=2360 MHz */ + uint32_t : 23; /* [31:9] reserved */ + } bit; + uint32_t reg; +} radio_frequency_t; + +#define RADIO_MAP_DEFAULT 0u /* channel n → 2400+n MHz */ +#define RADIO_MAP_BLE 1u /* channel n → 2360+n MHz */ + +/* TXPOWER — transmit power */ +typedef union { + struct { + int32_t TXPOWER : 8; /* [7:0] signed dBm: +8, +7, +6, +5, +4, +3, +2, + 0, -4, -8, -12, -16, -20, -40 */ + uint32_t : 24; /* [31:8] reserved */ + } bit; + uint32_t reg; +} radio_txpower_t; + +/* MODE — radio data rate and modulation */ +typedef union { + struct { + uint32_t MODE : 4; /* [3:0] 0=NRF_1Mbit 1=NRF_2Mbit 4=BLE_1Mbit … */ + uint32_t : 28; /* [31:4] reserved */ + } bit; + uint32_t reg; +} radio_mode_t; + +#define RADIO_MODE_NRF_1MBIT 0u +#define RADIO_MODE_NRF_2MBIT 1u +#define RADIO_MODE_BLE_1MBIT 4u diff --git a/justfile b/justfile new file mode 100644 index 0000000..d1adc05 --- /dev/null +++ b/justfile @@ -0,0 +1,52 @@ +# ── variables ────────────────────────────────────────────────────────────── + +image := "ptt-builder" +build_dir := "build" + +# podman takes priority over docker +engine := `command -v podman 2>/dev/null \ + || command -v docker 2>/dev/null \ + || { echo "error: podman or docker required" >&2; exit 1; }` + +# user-namespace mapping: podman rootless vs docker +user_ns := `command -v podman >/dev/null 2>&1 \ + && echo "--userns=keep-id" \ + || echo "--user $(id -u):$(id -g)"` + +# host CPU arch → container build arg +arch := `uname -m | sed 's/x86_64/amd64/; s/aarch64/arm64/'` + +# ── recipes ──────────────────────────────────────────────────────────────── + +# build container image (only rebuilt when Dockerfile changes) +image-build: + {{engine}} build \ + --build-arg TARGETARCH={{arch}} \ + --build-arg GCC_VERSION=13.2.rel1 \ + -t {{image}} . + +# compile firmware inside the container +build: image-build + {{engine}} run --rm \ + {{user_ns}} \ + -v "{{justfile_directory()}}:/src:z" \ + {{image}} \ + sh -c "cmake -B /src/{{build_dir}} -G Ninja \ + -DCMAKE_BUILD_TYPE=MinSizeRel /src \ + && ninja -C /src/{{build_dir}}" + +# flash firmware via pyocd on the host (requires USB / DAPLink) +flash: build + pyocd flash --target nrf52840 {{build_dir}}/firmware.hex + +# start GDB server for debugging +gdbserver: + pyocd gdbserver --target nrf52840 --port 3333 + +# remove build artifacts +clean: + rm -rf {{build_dir}} + +# remove build artifacts AND the container image +clean-all: clean + {{engine}} rmi {{image}} 2>/dev/null || true diff --git a/link/nrf52840.ld b/link/nrf52840.ld new file mode 100644 index 0000000..8ac0525 --- /dev/null +++ b/link/nrf52840.ld @@ -0,0 +1,66 @@ +/* + * nRF52840 — no SoftDevice + * Flash: 1 MB @ 0x00000000 + * RAM: 256 KB @ 0x20000000 + * + * Before first flash, erase the entire chip to remove any SoftDevice: + * pyocd erase --target nrf52840 --chip + */ + +MEMORY +{ + FLASH (rx) : ORIGIN = 0x00000000, LENGTH = 1024K + RAM (rwx) : ORIGIN = 0x20000000, LENGTH = 256K +} + +/* stack grows downward from the top of RAM */ +_estack = ORIGIN(RAM) + LENGTH(RAM); + +ENTRY(Reset_Handler) + +SECTIONS +{ + /* vector table must be at the start of Flash */ + .isr_vector : + { + KEEP(*(.isr_vector)) + . = ALIGN(4); + } > FLASH + + /* code and read-only data */ + .text : + { + *(.text .text.*) + *(.rodata .rodata.*) + . = ALIGN(4); + _etext = .; + } > FLASH + + /* load address of .data in Flash — Reset_Handler copies from here */ + _sidata = LOADADDR(.data); + + /* initialized variables: stored in Flash, run from RAM */ + .data : + { + _sdata = .; + *(.data .data.*) + . = ALIGN(4); + _edata = .; + } > RAM AT > FLASH + + /* zero-initialized variables: Reset_Handler clears this region */ + .bss (NOLOAD) : + { + _sbss = .; + *(.bss .bss.*) + *(COMMON) + . = ALIGN(4); + _ebss = .; + } > RAM + + /DISCARD/ : + { + *(.ARM.exidx*) + *(.gnu.linkonce.armexidx.*) + } +} diff --git a/src/fhss.c b/src/fhss.c new file mode 100644 index 0000000..7bd39d8 --- /dev/null +++ b/src/fhss.c @@ -0,0 +1,41 @@ +#include "fhss.h" +#include + +#define FHSS_CHANNELS 40u +#define FHSS_DWELL_MS 2u + +/* TODO: replace with a real shared secret before deployment */ +static const uint8_t shared_key[16] = { + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, +}; + +static uint32_t slot; + +void fhss_init(void) +{ + slot = 0u; +} + +uint8_t fhss_next_channel(void) +{ + uint8_t block[16] = {0}; + struct AES_ctx ctx; + + /* encode slot counter big-endian into the AES input block */ + block[0] = (uint8_t)(slot >> 24); + block[1] = (uint8_t)(slot >> 16); + block[2] = (uint8_t)(slot >> 8); + block[3] = (uint8_t)(slot ); + + AES_init_ctx(&ctx, shared_key); + AES_ECB_encrypt(&ctx, block); /* encrypts block in-place */ + + slot++; + return block[0] % FHSS_CHANNELS; +} + +void fhss_sync_tick(void) +{ + slot++; +} diff --git a/src/main.c b/src/main.c new file mode 100644 index 0000000..f77b10e --- /dev/null +++ b/src/main.c @@ -0,0 +1,15 @@ +#include "radio.h" +#include "fhss.h" +#include "power.h" + +int main(void) +{ + power_init(); + radio_init(); + fhss_init(); + + while (1) { + power_sleep_until_button(); + radio_tx_burst(); + } +} diff --git a/src/power.c b/src/power.c new file mode 100644 index 0000000..430d882 --- /dev/null +++ b/src/power.c @@ -0,0 +1,37 @@ +#include "power.h" +#include "regs.h" +#include +#include + +/* P0.02 on XIAO BLE — adjust to match your schematic */ +#define BUTTON_PIN 2u + +void power_init(void) +{ + /* DC/DC converter has lower quiescent current than the LDO */ + NRF_POWER->DCDCEN = 1u; + + NRF_GPIOTE->CONFIG[0] = (gpiote_config_t){ + .bit = { + .MODE = GPIOTE_MODE_EVENT, + .PSEL = BUTTON_PIN, + .PORT = 0u, + .POLARITY = GPIOTE_POL_LOTOHI, + } + }.reg; + + NRF_GPIOTE->INTENSET = (gpiote_inten_t){ .bit.IN0 = 1u }.reg; + + NVIC_EnableIRQ(GPIOTE_IRQn); +} + +void power_sleep_until_button(void) +{ + NRF_POWER->TASKS_LOWPWR = 1u; + __WFI(); +} + +void GPIOTE_IRQHandler(void) +{ + NRF_GPIOTE->EVENTS_IN[0] = 0u; +} diff --git a/src/radio.c b/src/radio.c new file mode 100644 index 0000000..5a26f54 --- /dev/null +++ b/src/radio.c @@ -0,0 +1,27 @@ +#include "radio.h" +#include "fhss.h" +#include "regs.h" +#include + +void radio_init(void) +{ + /* TODO: configure RADIO peripheral (MODE, PCNF0/1, BASE/PREFIX, CRC) */ +} + +void radio_set_channel(uint8_t ch) +{ + NRF_RADIO->FREQUENCY = (radio_frequency_t){ + .bit = { .FREQUENCY = ch, .MAP = RADIO_MAP_DEFAULT } + }.reg; +} + +void radio_tx(const uint8_t *data, uint8_t len) +{ + (void)data; (void)len; + /* TODO: load packet, enable TX, wait for END event, disable */ +} + +void radio_tx_burst(void) +{ + /* TODO: hop + TX loop driven by fhss_next_channel() */ +} diff --git a/src/startup.c b/src/startup.c new file mode 100644 index 0000000..41084d5 --- /dev/null +++ b/src/startup.c @@ -0,0 +1,168 @@ +#include + +/* linker script symbols */ +extern uint32_t _sidata; /* load address of .data in Flash */ +extern uint32_t _sdata; /* start of .data in RAM */ +extern uint32_t _edata; /* end of .data in RAM */ +extern uint32_t _sbss; +extern uint32_t _ebss; +extern uint32_t _estack; /* address equals initial SP value */ + +extern int main(void); + +void Reset_Handler(void); + +static void __attribute__((used)) Default_Handler(void) +{ + while (1); +} + +/* ARM Cortex-M4 core exceptions */ +void NMI_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void HardFault_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void MemManage_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void BusFault_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void UsageFault_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void SVC_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void DebugMon_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void PendSV_Handler(void) __attribute__((weak, alias("Default_Handler"))); +void SysTick_Handler(void) __attribute__((weak, alias("Default_Handler"))); + +/* nRF52840 peripheral IRQs (IRQ0–IRQ47) */ +void POWER_CLOCK_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void RADIO_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void UARTE0_UART0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void NFCT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void GPIOTE_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SAADC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TIMER0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TIMER1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TIMER2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void RTC0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TEMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void RNG_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void ECB_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void CCM_AAR_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void WDT_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void RTC1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void QDEC_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void COMP_LPCOMP_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI0_EGU0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI1_EGU1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI2_EGU2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI3_EGU3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI4_EGU4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SWI5_EGU5_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TIMER3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void TIMER4_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PWM0_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PDM_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void MWU_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PWM1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PWM2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SPIM2_SPIS2_SPI2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void RTC2_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void I2S_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void FPU_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void USBD_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void UARTE1_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void QSPI_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void CRYPTOCELL_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void PWM3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); +void SPIM3_IRQHandler(void) __attribute__((weak, alias("Default_Handler"))); + +/* ── vector table: 64 entries (16 core + 48 IRQs) ───────────────────────── */ + +typedef void (*vector_fn)(void); + +__attribute__((section(".isr_vector"), used)) +const vector_fn vectors[64] = { + /* 0 */ (vector_fn)&_estack, + /* 1 */ Reset_Handler, + /* 2 */ NMI_Handler, + /* 3 */ HardFault_Handler, + /* 4 */ MemManage_Handler, + /* 5 */ BusFault_Handler, + /* 6 */ UsageFault_Handler, + /* 7 */ 0, + /* 8 */ 0, + /* 9 */ 0, + /* 10 */ 0, + /* 11 */ SVC_Handler, + /* 12 */ DebugMon_Handler, + /* 13 */ 0, + /* 14 */ PendSV_Handler, + /* 15 */ SysTick_Handler, + + /* IRQ0 */ POWER_CLOCK_IRQHandler, + /* IRQ1 */ RADIO_IRQHandler, + /* IRQ2 */ UARTE0_UART0_IRQHandler, + /* IRQ3 */ SPIM0_SPIS0_TWIM0_TWIS0_SPI0_TWI0_IRQHandler, + /* IRQ4 */ SPIM1_SPIS1_TWIM1_TWIS1_SPI1_TWI1_IRQHandler, + /* IRQ5 */ NFCT_IRQHandler, + /* IRQ6 */ GPIOTE_IRQHandler, + /* IRQ7 */ SAADC_IRQHandler, + /* IRQ8 */ TIMER0_IRQHandler, + /* IRQ9 */ TIMER1_IRQHandler, + /* IRQ10 */ TIMER2_IRQHandler, + /* IRQ11 */ RTC0_IRQHandler, + /* IRQ12 */ TEMP_IRQHandler, + /* IRQ13 */ RNG_IRQHandler, + /* IRQ14 */ ECB_IRQHandler, + /* IRQ15 */ CCM_AAR_IRQHandler, + /* IRQ16 */ WDT_IRQHandler, + /* IRQ17 */ RTC1_IRQHandler, + /* IRQ18 */ QDEC_IRQHandler, + /* IRQ19 */ COMP_LPCOMP_IRQHandler, + /* IRQ20 */ SWI0_EGU0_IRQHandler, + /* IRQ21 */ SWI1_EGU1_IRQHandler, + /* IRQ22 */ SWI2_EGU2_IRQHandler, + /* IRQ23 */ SWI3_EGU3_IRQHandler, + /* IRQ24 */ SWI4_EGU4_IRQHandler, + /* IRQ25 */ SWI5_EGU5_IRQHandler, + /* IRQ26 */ TIMER3_IRQHandler, + /* IRQ27 */ TIMER4_IRQHandler, + /* IRQ28 */ PWM0_IRQHandler, + /* IRQ29 */ PDM_IRQHandler, + /* IRQ30 */ 0, /* reserved */ + /* IRQ31 */ 0, /* reserved */ + /* IRQ32 */ MWU_IRQHandler, + /* IRQ33 */ PWM1_IRQHandler, + /* IRQ34 */ PWM2_IRQHandler, + /* IRQ35 */ SPIM2_SPIS2_SPI2_IRQHandler, + /* IRQ36 */ RTC2_IRQHandler, + /* IRQ37 */ I2S_IRQHandler, + /* IRQ38 */ FPU_IRQHandler, + /* IRQ39 */ USBD_IRQHandler, + /* IRQ40 */ UARTE1_IRQHandler, + /* IRQ41 */ QSPI_IRQHandler, + /* IRQ42 */ CRYPTOCELL_IRQHandler, + /* IRQ43 */ 0, /* reserved */ + /* IRQ44 */ 0, /* reserved */ + /* IRQ45 */ PWM3_IRQHandler, + /* IRQ46 */ 0, /* reserved */ + /* IRQ47 */ SPIM3_IRQHandler, +}; + +/* ── Reset_Handler ───────────────────────────────────────────────────────── */ + +void Reset_Handler(void) +{ + /* copy .data initializers from Flash to RAM */ + uint32_t *src = &_sidata; + uint32_t *dst = &_sdata; + while (dst < &_edata) { + *dst++ = *src++; + } + + /* zero .bss */ + dst = &_sbss; + while (dst < &_ebss) { + *dst++ = 0u; + } + + main(); + while (1); +} diff --git a/vendor/CMSIS_5 b/vendor/CMSIS_5 new file mode 160000 index 0000000..55b1983 --- /dev/null +++ b/vendor/CMSIS_5 @@ -0,0 +1 @@ +Subproject commit 55b19837f5703e418ca37894d5745b1dc05e4c91 diff --git a/vendor/nrfx b/vendor/nrfx new file mode 160000 index 0000000..0883a27 --- /dev/null +++ b/vendor/nrfx @@ -0,0 +1 @@ +Subproject commit 0883a272c34004697dd56dfa44f6e2d0f8705689 diff --git a/vendor/tiny-aes-c b/vendor/tiny-aes-c new file mode 160000 index 0000000..2385675 --- /dev/null +++ b/vendor/tiny-aes-c @@ -0,0 +1 @@ +Subproject commit 23856752fbd139da0b8ca6e471a13d5bcc99a08d