diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..50a7d13 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,70 @@ +cmake_minimum_required(VERSION 3.20) + +# Define project name and supported languages +project(mixer-tutorial LANGUAGES C ASM) + +# Specify the ARM toolchain +set(CMAKE_SYSTEM_NAME Generic) +set(CMAKE_SYSTEM_PROCESSOR ARM) +set(ARM_TOOLCHAIN_PATH /usr/local/gcc-arm-none-eabi/gcc-arm-none-eabi-9-2020-q2-update) +set(CMAKE_C_COMPILER ${ARM_TOOLCHAIN_PATH}/bin/arm-none-eabi-gcc) +set(CMAKE_CXX_COMPILER ${ARM_TOOLCHAIN_PATH}/bin/arm-none-eabi-g++) +set(CMAKE_ASM_COMPILER ${ARM_TOOLCHAIN_PATH}/bin/arm-none-eabi-gcc) + +# Define build type +if(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug) +endif() + +# Compiler flags for Cortex-M4F (nRF52840) +set(CPU_FLAGS "-mcpu=cortex-m4 -mthumb -mfloat-abi=hard -mfpu=fpv4-sp-d16") +set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CPU_FLAGS} -Wall -Werror -Wno-unused-variable -Wno-unused-function") +set(CMAKE_ASM_FLAGS "${CMAKE_ASM_FLAGS} ${CPU_FLAGS}") + +add_compile_definitions( + NRF52840_XXAA + __nRF_FAMILY + ARM_MATH_CM4 + FLASH_PLACEMENT=1 + CONFIG_GPIO_AS_PINRESET + ASSERT_WARN_CT=0 + MX_CONFIG_FILE=${CMAKE_CURRENT_SOURCE_DIR}/tutorial/nRF52840/mixer_config.h + GPI_ARCH_PLATFORM=GPI_ARCH_BOARD_TUDNES_SHEPHERD_NRF52840FRAM_V13 + GPI_TRACE_MODE=GPI_TRACE_MODE_NO_TRACE + GPI_TRACE_BASE_SELECTION=GPI_TRACE_LOG_STANDARD + GPI_TRACE_BUFFER_ELEMENTS=64 +) + +# Include directories +include_directories( + src + src/mixer + src/gpi + tutorial/nRF52840/CMSIS_4/CMSIS/Include + tutorial/nRF52840/nRF/CMSIS/Device/Include +) + +# Source files +file(GLOB_RECURSE SOURCES + "src/gpi/*.c" + "src/gpi/arm/nordic/shepherd_nrf52/*.c" + "src/mixer/*.c" + "tutorial/nRF52840/main.c" +) + +# Add executable target +add_executable(${PROJECT_NAME}.elf ${SOURCES} + #tutorial/nRF52840/node_utils.h + #tutorial/nRF52840/leader.h + #tutorial/nRF52840/leader.c + #tutorial/nRF52840/follower.h + #tutorial/nRF52840/follower.c + src/gpi/arm/nordic/shepherd_nrf52/platform.c + #src/gpi/arm/nordic/shepherd_nrf52/stdio.c +) + +# Post-build commands to generate HEX and BIN files +add_custom_command(TARGET ${PROJECT_NAME}.elf POST_BUILD + COMMAND arm-none-eabi-objcopy -O ihex ${PROJECT_NAME}.elf ${PROJECT_NAME}.hex + COMMAND arm-none-eabi-objcopy -O binary ${PROJECT_NAME}.elf ${PROJECT_NAME}.bin +) diff --git a/src/gpi/.gitattributes b/src/gpi/.gitattributes new file mode 100644 index 0000000..6086e71 --- /dev/null +++ b/src/gpi/.gitattributes @@ -0,0 +1,7 @@ + +# see here for details: +# http://git-scm.com/book/en/v2/Customizing-Git-Git-Attributes + +# replace $Id$ in source files +*.h ident +*.c ident diff --git a/src/gpi/README.md b/src/gpi/README.md new file mode 100644 index 0000000..e75a413 --- /dev/null +++ b/src/gpi/README.md @@ -0,0 +1,13 @@ +# GPI + +NES Lab's Generic Platform Interface component (GPI) ATTENTION: This (nes-tud/gpi) is an internal project. Do not add new users without care! + +Main Repo: + +GitLab Mirror: + +Tutorials: + +Example Usage: [Mixer](https://github.com/nes-lab/Mixer) or [TrafficBench](https://github.com/nes-lab/TrafficBench) + +--- diff --git a/src/gpi/arm/armv7-m/interrupts.h b/src/gpi/arm/armv7-m/interrupts.h index 0a9ebc9..89be898 100644 --- a/src/gpi/arm/armv7-m/interrupts.h +++ b/src/gpi/arm/armv7-m/interrupts.h @@ -32,7 +32,7 @@ * * @brief basic interrupt handling * - * @version $Id: 2d2a1e6041b1edd60a76c899bcfb346f3ff4c1c9 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -70,12 +70,21 @@ // gpi_int_lock() locks all interrupts with priority >= GPI_ARM_INTLOCK_PRIORITY // (use NVIC_SetPriority() to define priorities). Use 0 to lock all interrupts. -// Setting GPI_ARM_INTLOCK_PRIORITY = 0x100 causes gpi_int_lock() to have no effect. +// Setting GPI_ARM_INTLOCK_PRIORITY = 1 << __NVIC_PRIO_BITS (i.e. _GPI_ARM_INTLOCK_PRIOMASK = 0x100) +// causes gpi_int_lock() to have no effect. +// NOTE: We distinguish between GPI_ARM_INTLOCK_PRIORITY and (internal) _GPI_ARM_INTLOCK_PRIOMASK +// because CMSIS' NVIC_SetPriority() use priority values 0, 1, ..., (1 << __NVIC_PRIO_BITS) - 1, +// i.e. the shift to the MSBs is hidden internally. GPI_ARM_INTLOCK_PRIORITY should use the same +// interpretation as NVIC_SetPriority(). #ifndef GPI_ARM_INTLOCK_PRIORITY #define GPI_ARM_INTLOCK_PRIORITY 0 -#else - ASSERT_CT_STATIC(GPI_ARM_INTLOCK_PRIORITY <= 0x100, GPI_ARM_INTLOCK_PRIORITY_is_invalid); #endif +#ifndef __NVIC_PRIO_BITS + #error "__NVIC_PRIO_BITS not defined (should come from CMSIS device header file)" +#endif +#define _GPI_ARM_INTLOCK_PRIOMASK (GPI_ARM_INTLOCK_PRIORITY << (8 - __NVIC_PRIO_BITS)) + +ASSERT_CT_STATIC(_GPI_ARM_INTLOCK_PRIOMASK <= 0x100, GPI_ARM_INTLOCK_PRIORITY_is_invalid); // Since ARMv7-M there are specific instructions (load/store exclusive) to support unblocking // synchronization. However, due to some implementation dependent details the usage of these @@ -148,11 +157,11 @@ static ALWAYS_INLINE int gpi_int_lock() // This is a matter of taste (it is not absolutely necessary if performance is secondary). __asm__ volatile ( -#if (GPI_ARM_INTLOCK_PRIORITY > 0) +#if (_GPI_ARM_INTLOCK_PRIOMASK > 0) "mrs %0, BASEPRI \n" // ie = __get_BASEPRI() "msr BASEPRI, %1 \n" // __set_BASEPRI(...) : "=&r"(ie) - : "r"(GPI_ARM_INTLOCK_PRIORITY & 0xFF) + : "r"(_GPI_ARM_INTLOCK_PRIOMASK & 0xFF) #else "mrs %0, PRIMASK \n" // ie = __get_PRIMASK() "cpsid i \n" // __set_PRIMASK(0) / __disable_irq() @@ -174,7 +183,7 @@ static ALWAYS_INLINE void gpi_int_unlock(int ie) __DMB(); // NOTE: we expect ie as it has been returned by gpi_int_lock() -#if (GPI_ARM_INTLOCK_PRIORITY > 0) +#if (_GPI_ARM_INTLOCK_PRIOMASK > 0) __set_BASEPRI(ie); #else __set_PRIMASK(ie); diff --git a/src/gpi/arm/armv7-m/olf.c b/src/gpi/arm/armv7-m/olf.c index dc9668d..f302c8f 100644 --- a/src/gpi/arm/armv7-m/olf.c +++ b/src/gpi/arm/armv7-m/olf.c @@ -32,7 +32,7 @@ * * @brief optimized low-level functions, tuned for ARMv7-M * - * @version $Id: f302c8fdb1a77a3c191593ecee65161a30bec7de $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann diff --git a/src/gpi/arm/armv7-m/olf.h b/src/gpi/arm/armv7-m/olf.h index ff949f0..3ad1fe2 100644 --- a/src/gpi/arm/armv7-m/olf.h +++ b/src/gpi/arm/armv7-m/olf.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2022, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ * * @brief optimized low-level functions, tuned for ARMv7-M * - * @version $Id: bc9ba0cdc35b85fc49fd6ed91e213d2c7afbf9c1 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -194,6 +194,8 @@ static ALWAYS_INLINE int_fast8_t gpi_get_lsb_32_core(uint32_t x, const int test_ // has no semantics when inline asm is used. if (test_zero) { + //ASSERT_CT_WARN(IS_CONST_EXPRESSION(return_if_zero)); + // NOTE: sub %0, %1, %2 (third line) is equivalent to mov %0, -%2 // implementing it this way allows arbitrary negative values for return_if_zero // (remember the limited possibilities for immediate constants at operand2) @@ -205,7 +207,7 @@ static ALWAYS_INLINE int_fast8_t gpi_get_lsb_32_core(uint32_t x, const int test_ "rbitne %0, %1 \n" "clzne %0, %0 \n" : "=r"(y) - : "r"(x), "i"((uint8_t)-return_if_zero) + : "r"(x), "ir"((uint8_t)-return_if_zero) : "cc" ); } diff --git a/src/gpi/arm/armv7-m/profile.c b/src/gpi/arm/armv7-m/profile.c index 1c0efeb..968217f 100644 --- a/src/gpi/arm/armv7-m/profile.c +++ b/src/gpi/arm/armv7-m/profile.c @@ -32,7 +32,7 @@ * * @brief support for program execution time profiling * - * @version $Id: 968217f20f6c8a7f83f806cd0a4a46d26216fb72 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann diff --git a/src/gpi/arm/armv7-m/profile.h b/src/gpi/arm/armv7-m/profile.h index 6e1ff54..00dae4a 100644 --- a/src/gpi/arm/armv7-m/profile.h +++ b/src/gpi/arm/armv7-m/profile.h @@ -32,7 +32,7 @@ * * @brief support for program execution time profiling * - * @version $Id: 00dae4aff2e1ddf362028560630a3778b7a0ecad $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann diff --git a/src/gpi/arm/armv7-m/trace.c b/src/gpi/arm/armv7-m/trace.c index 8ad7744..eec4e4e 100644 --- a/src/gpi/arm/armv7-m/trace.c +++ b/src/gpi/arm/armv7-m/trace.c @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2022, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ * * @brief generic ARMv7-M TRACE implementation * - * @version $Id: 69639c9304e789482f4cdf38a256b94162bf27e6 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -81,7 +81,7 @@ //************************************************************************************************** //***** Local (Static) Variables ******************************************************************* -static Gpi_Trace_Msg s_msg_queue[GPI_TRACE_BUFFER_ELEMENTS]; +static Gpi_Trace_Msg s_msg_queue[GPI_TRACE_BUFFER_NUM_ENTRIES]; static volatile unsigned int s_msg_queue_num_written = 0; static volatile unsigned int s_msg_queue_num_writing = 0; static volatile unsigned int s_msg_queue_num_read = 0; @@ -319,21 +319,44 @@ void gpi_trace_print_all_msgs() #endif #if !GPI_TRACE_OVERFLOW_ON_WRITE + static Gpi_Trace_Msg s_msg; msg = &s_msg; + + #if (0 != GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL) + + unsigned int num_read_max; + + #if (GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL > 0) + num_read_max = num_read + GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL; + #else + num_read_max = num_read + MIN(-GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL, s_msg_queue_num_written - num_read); + #endif + + #endif + #endif while (num_read != s_msg_queue_num_written) { #if !GPI_TRACE_OVERFLOW_ON_WRITE + + #if (0 != GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL) + if (num_read == num_read_max) + break; + #endif + gpi_memcpy_dma_aligned(msg, &s_msg_queue[num_read % NUM_ELEMENTS(s_msg_queue)], sizeof(Gpi_Trace_Msg)); unsigned int num_open = s_msg_queue_num_writing - num_read; if (num_open > NUM_ELEMENTS(s_msg_queue)) { - num_open -= NUM_ELEMENTS(s_msg_queue); + num_open = s_msg_queue_num_written - num_read; num_read += num_open; - + #if (0 != GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL) + num_read_max = num_read; + #endif + msg->msg = "!!! TRACE buffer overflow, %u message(s) lost !!!\n"; msg->var_args[0] = num_open; msg->timestamp = 0; diff --git a/src/gpi/arm/armv7-m/trace.h b/src/gpi/arm/armv7-m/trace.h index 52b9160..9537b7e 100644 --- a/src/gpi/arm/armv7-m/trace.h +++ b/src/gpi/arm/armv7-m/trace.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2022, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ * * @brief ARM specific TRACE settings * - * @version $Id: bf0cd24f27d85b9d5ca0f0f2fb84b617b37ec529 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -68,8 +68,8 @@ #define GPI_TRACE_VA_SIZE_MAX FIELD_SIZEOF(Gpi_Trace_Msg, var_args) // size of TRACE buffer (number of elements) -#ifndef GPI_TRACE_BUFFER_ELEMENTS - #define GPI_TRACE_BUFFER_ELEMENTS 16 +#ifndef GPI_TRACE_BUFFER_NUM_ENTRIES + #define GPI_TRACE_BUFFER_NUM_ENTRIES 16 #endif // TRACE buffer entry size @@ -89,6 +89,28 @@ // select whether TRACE buffer overflow detection is done on read or write side #define GPI_TRACE_OVERFLOW_ON_WRITE 0 +// maximum number of messages flushed by a single GPI_TRACE_FLUSH() call, 0 = unlimited +// @details +// In a single-threaded environment, each GPI_TRACE_FLUSH() call flushes at most +// GPI_TRACE_BUFFER_NUM_ENTRIES messages, as this is the maximum number of messages in the +// buffer (older messages get lost in case). In a multi-threaded environment, it is possible +// that new messages arrive while GPI_TRACE_FLUSH() is running, which can lead to the situation +// that a single GPI_TRACE_FLUSH() call outputs more than GPI_TRACE_BUFFER_NUM_ENTRIES messages +// (in the extreme case, GPI_TRACE_FLUSH() can run forever). This is critical as it can render +// it impossible to estimate the runtime of GPI_TRACE_FLUSH(). +// To overcome this problem, GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL can be used to limit the +// maximum number of messages that are output by a single GPI_TRACE_FLUSH() call. Further, +// if GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL is set to a negative value -N then it limits the +// number of messages to N and, additionally, processes only those messages that have already +// been in the buffer when GPI_TRACE_FLUSH() was entered (so messages arriving in parallel to +// GPI_TRACE_FLUSH() are not handled in the current call). +// NOTE: GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL is not considered if GPI_TRACE_OVERFLOW_ON_WRITE +// is active (TODO: change this). +// TODO: this macro may be of interest for multiple platforms, so maybe move it to gpi/trace.h +#ifndef GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL + #define GPI_TRACE_FLUSH_MAX_ENTRIES_PER_CALL GPI_TRACE_BUFFER_NUM_ENTRIES +#endif + // select if and how path part gets filtered out from file names #ifndef GPI_TRACE_FILTER_PATH #define GPI_TRACE_FILTER_PATH 1 diff --git a/src/gpi/arm/nordic/dpp2com/clocks.h b/src/gpi/arm/nordic/dpp2com/clocks.h index d940eac..30e35ee 100644 --- a/src/gpi/arm/nordic/dpp2com/clocks.h +++ b/src/gpi/arm/nordic/dpp2com/clocks.h @@ -32,7 +32,7 @@ * * @brief general-purpose slow, fast, and hybrid clock * - * @version $Id: 30e35eeb818af1c7a767d64261fc0b8c47f06f74 $ + * @version $Id$ * @date TODO * * @author Fabian Mager diff --git a/src/gpi/arm/nordic/dpp2com/platform.c b/src/gpi/arm/nordic/dpp2com/platform.c index 5a8e68c..c65cf64 100644 --- a/src/gpi/arm/nordic/dpp2com/platform.c +++ b/src/gpi/arm/nordic/dpp2com/platform.c @@ -32,7 +32,7 @@ * * @brief platform interface functions * - * @version $Id: ec37c2eac77ba05e9bf52731d0c5864a14056e67 $ + * @version $Id$ * @date TODO * * @author Fabian Mager @@ -477,13 +477,13 @@ void gpi_platform_init() // if VHT: use PPI to connect RTC->EVENTS_TICK to TIMER->TASKS_CAPTURE #if GPI_HYBRID_CLOCK_USE_VHT - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].EEP = + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].EEP = (uintptr_t)&(_gpi_clocks_rtc->EVENTS_TICK); - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].TEP = - (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_HYBRID_CLOCK_NRF_CAPTURE_REG]); + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].TEP = + (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]); - NRF_PPI->CHENSET = BV(GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL); + NRF_PPI->CHENSET = BV(GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); #endif diff --git a/src/gpi/arm/nordic/dpp2com/platform.h b/src/gpi/arm/nordic/dpp2com/platform.h index 1c0e970..34ee348 100644 --- a/src/gpi/arm/nordic/dpp2com/platform.h +++ b/src/gpi/arm/nordic/dpp2com/platform.h @@ -32,7 +32,7 @@ * * @brief platform interface functions, specific for DPP2 Com board based on nRF52840 * - * @version $Id: 34ee348a1963feb4d7f0b188a5c9de69873f744a $ + * @version $Id$ * @date TODO * * @author Fabian Mager diff --git a/src/gpi/arm/nordic/nrf52840/clocks.c b/src/gpi/arm/nordic/nrf528xx/clocks.c similarity index 95% rename from src/gpi/arm/nordic/nrf52840/clocks.c rename to src/gpi/arm/nordic/nrf528xx/clocks.c index 399e296..d5061a4 100644 --- a/src/gpi/arm/nordic/nrf52840/clocks.c +++ b/src/gpi/arm/nordic/nrf528xx/clocks.c @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/clocks.c + * @file gpi/arm/nordic/nrf528xx/clocks.c * * @brief general-purpose slow, fast, and hybrid clock * - * @version $Id: eb1a68c44b1cfd1addcdbe956d3fa73b783894d6 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -58,15 +58,15 @@ #include "gpi/resource_check.h" -GPI_RESOURCE_RESERVE_SHARED(NRF_TIMER, GPI_FAST_CLOCK_NRF_TIMER); -GPI_RESOURCE_RESERVE(NRF_TIMER_CC, GPI_FAST_CLOCK_NRF_TIMER, GPI_FAST_CLOCK_NRF_CAPTURE_REG); +GPI_RESOURCE_RESERVE_SHARED(NRF_TIMER, GPI_ARM_NRF_FAST_CLOCK_TIMER); +GPI_RESOURCE_RESERVE(NRF_TIMER_CC, GPI_ARM_NRF_FAST_CLOCK_TIMER, GPI_ARM_NRF_FAST_CLOCK_CAPTURE_REG); #if GPI_HYBRID_CLOCK_USE_VHT - GPI_RESOURCE_RESERVE(NRF_PPI_CH, GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL); - GPI_RESOURCE_RESERVE(NRF_TIMER_CC, GPI_FAST_CLOCK_NRF_TIMER, GPI_HYBRID_CLOCK_NRF_CAPTURE_REG); + GPI_RESOURCE_RESERVE(NRF_PPI_CH, GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); + GPI_RESOURCE_RESERVE(NRF_TIMER_CC, GPI_ARM_NRF_FAST_CLOCK_TIMER, GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG); #endif -GPI_RESOURCE_RESERVE_SHARED(NRF_RTC, GPI_SLOW_CLOCK_NRF_RTC); +GPI_RESOURCE_RESERVE_SHARED(NRF_RTC, GPI_ARM_NRF_SLOW_CLOCK_RTC); //************************************************************************************************** //***** Local Defines and Consts ******************************************************************* @@ -180,7 +180,7 @@ Gpi_Hybrid_Reference gpi_tick_hybrid_reference() // from HCLK64M), 8 nops plus the execution time of the COUNTER read should be save. __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); __NOP(); - fast = _gpi_clocks_fast_timer->CC[GPI_HYBRID_CLOCK_NRF_CAPTURE_REG]; + fast = _gpi_clocks_fast_timer->CC[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]; } while (_gpi_clocks_rtc->EVENTS_TICK); diff --git a/src/gpi/arm/nordic/nrf52840/clocks.h b/src/gpi/arm/nordic/nrf528xx/clocks.h similarity index 84% rename from src/gpi/arm/nordic/nrf52840/clocks.h rename to src/gpi/arm/nordic/nrf528xx/clocks.h index f32c6d2..c32ea9c 100644 --- a/src/gpi/arm/nordic/nrf52840/clocks.h +++ b/src/gpi/arm/nordic/nrf528xx/clocks.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/clocks.h + * @file gpi/arm/nordic/nrf528xx/clocks.h * * @brief general-purpose slow, fast, and hybrid clock * - * @version $Id: 27ef442aba60fd2729a46cc6e48d7e9b596408e5 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,8 +45,8 @@ **************************************************************************************************/ -#ifndef __GPI_ARM_nRF52840_CLOCKS_H__ -#define __GPI_ARM_nRF52840_CLOCKS_H__ +#ifndef __GPI_ARM_nRF528xx_CLOCKS_H__ +#define __GPI_ARM_nRF528xx_CLOCKS_H__ //************************************************************************************************** //***** Includes *********************************************************************************** @@ -77,25 +77,25 @@ //************************************************************************************************** //***** Local (Private) Defines and Consts ********************************************************* -#ifndef GPI_FAST_CLOCK_NRF_TIMER - #define GPI_FAST_CLOCK_NRF_TIMER 0 +#ifndef GPI_ARM_NRF_FAST_CLOCK_TIMER + #define GPI_ARM_NRF_FAST_CLOCK_TIMER 0 #endif -#ifndef GPI_FAST_CLOCK_NRF_CAPTURE_REG - #define GPI_FAST_CLOCK_NRF_CAPTURE_REG 0 +#ifndef GPI_ARM_NRF_FAST_CLOCK_CAPTURE_REG + #define GPI_ARM_NRF_FAST_CLOCK_CAPTURE_REG 0 #endif -#ifndef GPI_SLOW_CLOCK_NRF_RTC - #define GPI_SLOW_CLOCK_NRF_RTC 0 +#ifndef GPI_ARM_NRF_SLOW_CLOCK_RTC + #define GPI_ARM_NRF_SLOW_CLOCK_RTC 0 #endif #ifndef GPI_HYBRID_CLOCK_USE_VHT - #define GPI_HYBRID_CLOCK_USE_VHT 0 // otherwise HYBRID_CLOCK === FAST_CLOCK + #define GPI_HYBRID_CLOCK_USE_VHT 0 // otherwise HYBRID_CLOCK === FAST_CLOCK #endif -#ifndef GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL - #define GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL 0 // needed only if GPI_HYBRID_CLOCK_USE_VHT is set +#ifndef GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL + #define GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL 0 // needed only if GPI_HYBRID_CLOCK_USE_VHT is set #endif -#ifndef GPI_HYBRID_CLOCK_NRF_CAPTURE_REG - #define GPI_HYBRID_CLOCK_NRF_CAPTURE_REG 1 // needed only if GPI_HYBRID_CLOCK_USE_VHT is set +#ifndef GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG + #define GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG 1 // needed only if GPI_HYBRID_CLOCK_USE_VHT is set #endif //************************************************************************************************** @@ -123,29 +123,29 @@ typedef struct Gpi_Hybrid_Reference_tag //***** Global Variables *************************************************************************** static volatile typeof(*NRF_TIMER0) * const _gpi_clocks_fast_timer = - #if (0 == GPI_FAST_CLOCK_NRF_TIMER) + #if (0 == GPI_ARM_NRF_FAST_CLOCK_TIMER) NRF_TIMER0; - #elif (1 == GPI_FAST_CLOCK_NRF_TIMER) + #elif (1 == GPI_ARM_NRF_FAST_CLOCK_TIMER) NRF_TIMER1; - #elif (2 == GPI_FAST_CLOCK_NRF_TIMER) + #elif (2 == GPI_ARM_NRF_FAST_CLOCK_TIMER) NRF_TIMER2; - #elif (3 == GPI_FAST_CLOCK_NRF_TIMER) + #elif (3 == GPI_ARM_NRF_FAST_CLOCK_TIMER) NRF_TIMER3; - #elif (4 == GPI_FAST_CLOCK_NRF_TIMER) + #elif (4 == GPI_ARM_NRF_FAST_CLOCK_TIMER) NRF_TIMER4; #else - #error GPI_FAST_CLOCK_NRF_TIMER is invalid + #error GPI_ARM_NRF_FAST_CLOCK_TIMER is invalid #endif static volatile typeof(*NRF_RTC0) * const _gpi_clocks_rtc = - #if (0 == GPI_SLOW_CLOCK_NRF_RTC) + #if (0 == GPI_ARM_NRF_SLOW_CLOCK_RTC) NRF_RTC0; - #elif (1 == GPI_SLOW_CLOCK_NRF_RTC) + #elif (1 == GPI_ARM_NRF_SLOW_CLOCK_RTC) NRF_RTC1; - #elif (2 == GPI_SLOW_CLOCK_NRF_RTC) + #elif (2 == GPI_ARM_NRF_SLOW_CLOCK_RTC) NRF_RTC2; #else - #error GPI_SLOW_CLOCK_NRF_RTC is invalid + #error GPI_ARM_NRF_SLOW_CLOCK_RTC is invalid #endif //************************************************************************************************** @@ -167,7 +167,7 @@ static volatile typeof(*NRF_RTC0) * const _gpi_clocks_rtc = //************************************************************************************************** //***** Implementations of Inline Functions ******************************************************** -static ALWAYS_INLINE Gpi_Slow_Tick_Native gpi_tick_slow_native() +static ALWAYS_INLINE Gpi_Slow_Tick_Native gpi_tick_slow_native(void) { // ATTENTION: counter register is asynchronous to the CPU clock, but the RTC peripheral // synchronizes reads by itself (see spec. 4413_417 v1.0 page 340 "Reading the COUNTER @@ -178,7 +178,7 @@ static ALWAYS_INLINE Gpi_Slow_Tick_Native gpi_tick_slow_native() //************************************************************************************************** -static ALWAYS_INLINE Gpi_Fast_Tick_Native gpi_tick_fast_native() +static ALWAYS_INLINE Gpi_Fast_Tick_Native gpi_tick_fast_native(void) { // the counter register is not directly accessible // -> we trigger a capture event and read the value from the capture register @@ -188,13 +188,13 @@ static ALWAYS_INLINE Gpi_Fast_Tick_Native gpi_tick_fast_native() // timestamp is taken before, during, or after the interrupt as long as it stems from the // interval between function entry and return. Hence, we do not need a locking mechanism. - _gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_FAST_CLOCK_NRF_CAPTURE_REG] = 1; - return _gpi_clocks_fast_timer->CC[GPI_FAST_CLOCK_NRF_CAPTURE_REG]; + _gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_FAST_CLOCK_CAPTURE_REG] = 1; + return _gpi_clocks_fast_timer->CC[GPI_ARM_NRF_FAST_CLOCK_CAPTURE_REG]; } //************************************************************************************************** -static ALWAYS_INLINE Gpi_Fast_Tick_Extended gpi_tick_fast_extended() +static ALWAYS_INLINE Gpi_Fast_Tick_Extended gpi_tick_fast_extended(void) { ASSERT_CT(sizeof(Gpi_Fast_Tick_Extended) == sizeof(Gpi_Fast_Tick_Native)); @@ -217,7 +217,7 @@ static ALWAYS_INLINE Gpi_Hybrid_Tick gpi_tick_fast_to_hybrid(Gpi_Fast_Tick_Nativ //************************************************************************************************** #if !GPI_HYBRID_CLOCK_USE_VHT -static ALWAYS_INLINE Gpi_Hybrid_Reference gpi_tick_hybrid_reference() +static ALWAYS_INLINE Gpi_Hybrid_Reference gpi_tick_hybrid_reference(void) { // use the full function(ality) if format extension/conversion is necessary // ASSERT_CT(sizeof(Gpi_Hybrid_Tick) == sizeof(Gpi_Fast_Tick_Native)); @@ -234,7 +234,7 @@ static ALWAYS_INLINE Gpi_Hybrid_Reference gpi_tick_hybrid_reference() #endif // GPI_HYBRID_CLOCK_USE_VHT //************************************************************************************************** -static ALWAYS_INLINE Gpi_Hybrid_Tick gpi_tick_hybrid() +static ALWAYS_INLINE Gpi_Hybrid_Tick gpi_tick_hybrid(void) { return gpi_tick_fast_to_hybrid(gpi_tick_fast_native()); } @@ -284,4 +284,4 @@ static ALWAYS_INLINE uint32_t gpi_tick_fast_to_us(Gpi_Fast_Tick_Extended ticks) //************************************************************************************************** //************************************************************************************************** -#endif // __GPI_ARM_nRF52840_CLOCKS_H__ +#endif // __GPI_ARM_nRF528xx_CLOCKS_H__ diff --git a/src/gpi/arm/nordic/nrf52840/interrupts.h b/src/gpi/arm/nordic/nrf528xx/interrupts.h similarity index 100% rename from src/gpi/arm/nordic/nrf52840/interrupts.h rename to src/gpi/arm/nordic/nrf528xx/interrupts.h diff --git a/src/gpi/arm/nordic/nrf52840/olf.h b/src/gpi/arm/nordic/nrf528xx/olf.h similarity index 94% rename from src/gpi/arm/nordic/nrf52840/olf.h rename to src/gpi/arm/nordic/nrf528xx/olf.h index 3552910..f737561 100644 --- a/src/gpi/arm/nordic/nrf52840/olf.h +++ b/src/gpi/arm/nordic/nrf528xx/olf.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/olf.h + * @file gpi/arm/nordic/nrf528xx/olf.h * - * @brief optimized low-level functions, tuned for Nordic nRF52840 + * @brief optimized low-level functions, tuned for Nordic nRF528xx series * - * @version $Id: 5308f0562ff7081aa9ab5e5aebd3a5e894dc9d01 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,8 +45,8 @@ **************************************************************************************************/ -#ifndef __GPI_ARM_nRF52840_OLF_H__ -#define __GPI_ARM_nRF52840_OLF_H__ +#ifndef __GPI_ARM_nRF528xx_OLF_H__ +#define __GPI_ARM_nRF528xx_OLF_H__ //************************************************************************************************** //***** Includes *********************************************************************************** @@ -127,4 +127,4 @@ static ALWAYS_INLINE void gpi_memmove_dma_inline(void *dest, const void *src, si //************************************************************************************************** //************************************************************************************************** -#endif // __GPI_ARM_nRF52840_OLF_H__ +#endif // __GPI_ARM_nRF528xx_OLF_H__ diff --git a/src/gpi/arm/nordic/nrf528xx/platform.c b/src/gpi/arm/nordic/nrf528xx/platform.c new file mode 100644 index 0000000..0928760 --- /dev/null +++ b/src/gpi/arm/nordic/nrf528xx/platform.c @@ -0,0 +1,206 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/nrf528xx/platform.c + * + * @brief common nRF528xx platform interface functions + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ +//***** Trace Settings ***************************************************************************** + + + +//************************************************************************************************** +//**** Includes ************************************************************************************ + +#include "gpi/tools.h" +#include "gpi/platform.h" +#include "gpi/interrupts.h" + +#include "platform_internal.h" + +#include + +//************************************************************************************************** +//***** Local Defines and Consts ******************************************************************* + +#define WARNING(msg) WARNING2(GCC warning msg) +#define WARNING2(msg) _Pragma (#msg) + +// issue default value warnings (see platform.h for details) +#ifdef _GPI_STDOUT_UART_BAUDRATE_DEFAULT_WARNING + WARNING(_GPI_STDOUT_UART_BAUDRATE_DEFAULT_WARNING) +#endif +#ifdef _GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY_DEFAULT_WARNING + WARNING(_GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY_DEFAULT_WARNING) +#endif +#ifdef _GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS_DEFAULT_WARNING + WARNING(_GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS_DEFAULT_WARNING) +#endif +#ifdef _GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE_DEFAULT_WARNING + WARNING(_GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE_DEFAULT_WARNING) +#endif + +//************************************************************************************************** +//***** Local Typedefs and Class Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Forward Declarations *********************************************************************** + + + +//************************************************************************************************** +//***** Local (Static) Variables ******************************************************************* + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + +uint_fast8_t gpi_wakeup_event = 0; + +//************************************************************************************************** +//***** Local Functions **************************************************************************** + + + +//************************************************************************************************** +//***** Global Functions *************************************************************************** + +void gpi_sleep() +{ + // disable interrupts, set PRIMASK = 1 + gpi_int_disable(); + + // mark that CPU comes from power-down + // this flag can be evaluated by the application + // NOTE: to be meaningful, the first ISR taken after power-up should clear it + gpi_wakeup_event = 1; + + // set control registers such that CPU will wake-up but not enter ISR, i.e., program returns here + // (see ARM Cortex-M4 Generic User Guide (DUI 0553A ID121610) "2.5.2 Wakeup from sleep mode" for details) + // NOTE: PRIMASK has already been set to 1 by gpi_int_disable() above +// __set_FAULTMASK(0); +// __set_PRIMASK(1); + + // enter power-down (if no IRQ pending) + // NOTE: enabled interrupts work as wake-up events even if PRIMASK = 0 + // NOTE: SCR settings are assumed to be configured by the application (fitting her needs) + __WFI(); + + // sleep... + + // restore standard behavior + // NOTE: PRIMASK = 0 reenables interrupts. In consequence, pending IRQ(s) will be taken. + __set_PRIMASK(0); +} + +//************************************************************************************************** + +void gpi_nrf_uicr_erase() +{ + // set erase-enable mode + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Een); + + // erase UICR + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + NRF_NVMC->ERASEUICR = BV_BY_NAME(NVMC_ERASEUICR_ERASEUICR, Erase); + + // go back to read-only mode + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); + + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); +} + +//************************************************************************************************** + +void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size) +{ + dest = MAX(dest, sizeof(NRF_UICR->CUSTOMER)); + size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - dest); + + if (0 == size) + return; + + // set write-enable mode + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Wen); + + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + + // write UICR words + while (size > 0) + { + uint_fast8_t n = dest & 0x3; + uint32_t t; + + // assemble aligned 32-bit data word + t = 0xffffffff; + memcpy((uint8_t*)&t + n, src, MIN(4 - n, size)); + n = MIN(4 - n, size); + + // write data word + // ATTENTION: there seems to be some timing issue with READYNEXT (observed with + // optimization level 3 enabled). We add a tiny sleep period to circumvent that. + // The sleep does not hurt because writing a single word takes much more time + // anyhow (4413_417 v1.0: typ. 41us). + while (!BV_TEST_BY_NAME(NRF_NVMC->READYNEXT, NVMC_READYNEXT_READYNEXT, Ready)); + NRF_UICR->CUSTOMER[dest >> 2] = t; + gpi_micro_sleep(2); + + src = (const uint8_t*)src + n; + dest += n; + size -= n; + } + + // go back to read-only mode + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); + NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); + + while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); +} + +//************************************************************************************************** +//************************************************************************************************** diff --git a/src/gpi/arm/nordic/nrf528xx/platform.h b/src/gpi/arm/nordic/nrf528xx/platform.h new file mode 100644 index 0000000..c3d032a --- /dev/null +++ b/src/gpi/arm/nordic/nrf528xx/platform.h @@ -0,0 +1,216 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/nrf528xx/platform.h + * + * @brief common nRF528xx-specific platform interface functions + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ + +#ifndef __GPI_ARM_nRF528xx_PLATFORM_H__ +#define __GPI_ARM_nRF528xx_PLATFORM_H__ + +//************************************************************************************************** +//***** Includes *********************************************************************************** + +#include "gpi/platform_spec.h" + +#include "gpi/tools.h" + +#include + +#include +#include + +//************************************************************************************************** +//***** Global (Public) Defines and Consts ********************************************************* + +// NOTE: Regarding default value warnings, we do not trigger #warning directly because this would +// issue the warning with every file including platform.h, which unnecessarily clutters the output. +// Instead, we store each warning message in a macro and print it during compilation of a related +// implementation file, e.g. platform.c. + +// stdio UART baudrate +// ATTENTION: Without hardware flow control, baudrates >= 115200 can cause data losses at PCA10056's +// interface MCU under full UART load (e.g. printf() loop). Hence, we strongly recommend to enable +// hardware flow control if high baudrates are used (see GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE). +// (Without giving any guarantees, it seems that baudrates <= 57600 are safe without flow control.) +#ifndef GPI_STDOUT_UART_BAUDRATE + #define GPI_STDOUT_UART_BAUDRATE 115200 + #define _GPI_STDOUT_UART_BAUDRATE_DEFAULT_WARNING \ + "GPI_STDOUT_UART_BAUDRATE undefined, default = 115200" +#endif + +// stdout UART flow control mode: +// 0 = disabled (no flow control) +// 1 = CTS with pull-down (hardware flow control, Tx unlocked while CTS not driven by receiver) +// 3 = CTS with pull-up (hardware flow control, Tx locked while CTS not driven by receiver) +#ifndef GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE + #define GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE 0 +#endif + +// decide wether stdout/stderr functionality uses interrupt driven async I/O +#ifndef GPI_STDOUT_INTERRUPT_ENABLED + #define GPI_STDOUT_INTERRUPT_ENABLED 0 +#endif + +// settings for interrupt driven stdout/stderr +#if GPI_STDOUT_INTERRUPT_ENABLED + + // interrupt priority level + // must be at least as high as highest priority ISR that writes to stdout/stderr + #ifndef GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY + #define GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY 1 + #define _GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY_DEFAULT_WARNING \ + "GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY undefined, default = 1" + #endif + + // number of slots in the transmit buffer pool + // must be >= possible number of nested print calls + // higher number increases decoupling capabilities + // choosing a power of 2 is most efficient + #ifndef GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS + #define GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS 16 + #define _GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS_DEFAULT_WARNING \ + "GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS undefined, default = 16" + #endif + + // size of single transmit buffer slot + // higher number increases efficiency for print functions that are not split into putchar() calls + // (typically (f)puts() is good, with (f)printf() it depends) + #ifndef GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE + #define GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE 4 + #define _GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE_DEFAULT_WARNING \ + "GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE undefined, default = 4" + #endif + +#endif + +//************************************************************************************************** +//***** Local (Private) Defines and Consts ********************************************************* + + + +//************************************************************************************************** +//***** Forward Class and Struct Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Global Typedefs and Class Declarations ***************************************************** + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + +// mark that CPU comes from power-down +// this flag can be evaluated by the application +// NOTE: to be meaningful, the first ISR taken after power-up should clear it +extern uint_fast8_t gpi_wakeup_event; + +//************************************************************************************************** +//***** Prototypes of Global Functions ************************************************************* + +#ifdef __cplusplus + extern "C" { +#endif + +// UICR access functions +// UICR = User Information Configuration Registers, see spec. for details +// ATTENTION: Writing to UICR or flash requires NVMC->CONFIG.WEN to be set which in turn +// invalidates the instruction cache (permanently). Besides that, UICR updates take effect +// only after reset (spec. 4413_417 v1.0 4.3.3 page 24). Therefore it is highly recommended +// to do a soft reset (e.g., by calling NVIC_SystemReset()) after updating flash or UICR. +static void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size); +void gpi_nrf_uicr_erase(); +void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size); + +// standard C library does not provide getsn(), so we do it +// NOTE: gets() has been removed in C11. The official recommendation is to use fgets() instead. +// But: The latter does not remove the trailing newline, so take care with that. +#if GPI_ARCH_IS_OS(NONE) + void gpi_stdin_flush(); + char* getsn(char* s, size_t size); +#endif + +#ifdef __cplusplus + } +#endif + +//************************************************************************************************** +//***** Implementations of Inline Functions ******************************************************** + +static inline void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size) +{ + src = MAX(src, sizeof(NRF_UICR->CUSTOMER)); + size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - src); + + // Starting with GCC 11, the compiler can generate warnings when calls to string manipulation + // functions such as memcpy() or strcpy() are determined to overflow the destination buffer + // or to read past the end of the source string/buffer (see -Wstringop-...). For this purpose, + // the compiler has to determine the corresponding buffer sizes, and it seems that there are + // some open issues related to this. For example, with GCC 11.2 (SEGGER Embedded Studio 6.34) + // the memcpy() call below throws the warning + // "warning: ‘memcpy’ reading 4 bytes from a region of size 0 [-Wstringop-overread]" because + // the compiler is unable to determine the real size of the source buffer. Unfortunately, + // the same GCC version has issues regarding the temporary deactivation of this warning. To + // work around this, we catch GCC version 11 explicitly and give higher versions another try. + + #if __GNUC__ > 11 + #pragma GCC diagnostic push + #pragma GCC diagnostic ignored "-Wstringop-overread" + memcpy(dest, (uint8_t*)&(NRF_UICR->CUSTOMER) + src, size); + #pragma GCC diagnostic pop + #elif __GNUC__ == 11 + uint8_t *d = dest; + uint8_t *s = (uint8_t*)&(NRF_UICR->CUSTOMER) + src; + while (size--) + *d++ = *s++; + #else + memcpy(dest, (uint8_t*)&(NRF_UICR->CUSTOMER) + src, size); + #endif +} + +//************************************************************************************************** +//************************************************************************************************** + +#endif // __GPI_ARM_nRF528xx_PLATFORM_H__ diff --git a/src/gpi/arm/nordic/nrf528xx/platform_internal.h b/src/gpi/arm/nordic/nrf528xx/platform_internal.h new file mode 100644 index 0000000..de20dac --- /dev/null +++ b/src/gpi/arm/nordic/nrf528xx/platform_internal.h @@ -0,0 +1,311 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/nrf528xx/platform_internal.h + * + * @brief internal nRF528xx platform functions + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ + +#ifndef __GPI_ARM_nRF528xx_PLATFORM_INTERNAL_H__ +#define __GPI_ARM_nRF528xx_PLATFORM_INTERNAL_H__ + +//************************************************************************************************** +//***** Includes *********************************************************************************** + +#include "gpi/platform_spec.h" + +#include "gpi/tools.h" +#include "gpi/platform.h" // get internal settings +#include "gpi/interrupts.h" + +#include + +#include + +//************************************************************************************************** +//***** Global (Public) Defines and Consts ********************************************************* + + + +//************************************************************************************************** +//***** Local (Private) Defines and Consts ********************************************************* + +// bitfield macros for CMSIS register definitions + +#define BV_BY_NAME(field, value) ((field ## _ ## value << field ## _Pos) & field ## _Msk) +#define BV_BY_VALUE(field, value) (((value) << field ## _Pos) & field ## _Msk) +//#define BV_BY_NAME(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) +//#define BV_BY_VALUE(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) + +// with intermediate macro expansion +#define BV_BY_NAME_PREEXP(field, value) BV_BY_NAME(field, value) +#define BV_BY_VALUE_PREEXP(field, value) BV_BY_VALUE(field, value) + +#define BV_TEST_BY_NAME(reg, field, value) (BV_BY_NAME(field, value) == ((reg) & field ## _Msk)) +#define BV_TEST_BY_VALUE(reg, field, value) (BV_BY_VALUE(field, value) == ((reg) & field ## _Msk)) + +//************************************************************************************************** +//***** Forward Class and Struct Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Global Typedefs and Class Declarations ***************************************************** + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + + + +//************************************************************************************************** +//***** Prototypes of Global Functions ************************************************************* + +#ifdef __cplusplus + extern "C" { +#endif + + + +#ifdef __cplusplus + } +#endif + +//************************************************************************************************** +//***** Implementations of Inline Functions ******************************************************** + +// init (reset) CPU core to defined state +// this function can be moved to generic ARM code if helpful +static inline void core_init() +{ + // NOTE: some of the regs are already initialized for sure (since program arrived here) + + gpi_int_disable(); + __set_BASEPRI(0); + __set_FAULTMASK(0); + __set_CONTROL(0); // no floating point-context, use MSP, privileged level + __DSB(); + __ISB(); + + // disable MPU + MPU->CTRL = 0; + + // TODO: enable FPU if requested + + // TODO: setup Traps and Fault Exception Handlers (if requested) + // -> regs. in System Control Block + + // TODO: setup SysTick timer if requested +} + +//************************************************************************************************** + +// init UART +// TODO: maybe make it a public function in platform.h (params: baudrate, flags (like HW flow control)) +// NOTE: if function is inlined and baudrate is constant then it gets well optimized +static inline void uart_init(uint32_t baudrate) +{ + assert(baudrate <= 1000000); // see spec. UARTE features + + // TODO: if enabled: STOPTX/RX + + // disable UART during reconfiguration + NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Disabled); + + // configure pins + // NOTE: UART gets initialized even if no external pins are used + // because other functions may rely on it + { + __typeof__(NRF_P0) port; + + // TXD + #ifdef _GPI_ARM_nRF_UART_TXD_PORT + port = (_GPI_ARM_nRF_UART_TXD_PORT) ? NRF_P1 : NRF_P0; + port->OUTSET = BV(_GPI_ARM_nRF_UART_TXD_PIN); + port->PIN_CNF[_GPI_ARM_nRF_UART_TXD_PIN] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_UARTE0->PSEL.TXD = + BV_BY_VALUE(UARTE_PSEL_TXD_PORT, _GPI_ARM_nRF_UART_TXD_PORT) | + BV_BY_VALUE(UARTE_PSEL_TXD_PIN, _GPI_ARM_nRF_UART_TXD_PIN) | + BV_BY_NAME(UARTE_PSEL_TXD_CONNECT, Connected); + #else + NRF_UARTE0->PSEL.TXD = + BV_BY_NAME(UARTE_PSEL_TXD_CONNECT, Disconnected); + #endif + + // RXD + #ifdef _GPI_ARM_nRF_UART_RXD_PORT + port = (_GPI_ARM_nRF_UART_RXD_PORT) ? NRF_P1 : NRF_P0; + NRF_P0->PIN_CNF[_GPI_ARM_nRF_UART_RXD_PIN] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_UARTE0->PSEL.RXD = + BV_BY_VALUE(UARTE_PSEL_RXD_PORT, _GPI_ARM_nRF_UART_RXD_PORT) | + BV_BY_VALUE(UARTE_PSEL_RXD_PIN, _GPI_ARM_nRF_UART_RXD_PIN) | + BV_BY_NAME(UARTE_PSEL_RXD_CONNECT, Connected); + #else + NRF_UARTE0->PSEL.RXD = + BV_BY_NAME(UARTE_PSEL_RXD_CONNECT, Disconnected); + #endif + + // RTS + // So far, we do not support flow control in receive direction (= stdin). But: + // ATTENTION: Interface MCU on PCA10056 uses RTS to perform dynamic flow control detection + // (see Dev. Kit User Guide 4440_050 v1.1 section 7.2.1 for details). + // Pull RTS low / high to signal that flow control is used / unused. + #if GPI_ARCH_IS_BOARD(nRF_PCA10056) + port = (_GPI_ARM_nRF_UART_RTS_PORT) ? NRF_P1 : NRF_P0; + #if GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE + port->OUTCLR = BV(_GPI_ARM_nRF_UART_RTS_PIN); + port->PIN_CNF[_GPI_ARM_nRF_UART_RTS_PIN] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + #else + port->OUTSET = BV(_GPI_ARM_nRF_UART_RTS_PIN); + port->PIN_CNF[_GPI_ARM_nRF_UART_RTS_PIN] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + #endif + #endif + NRF_UARTE0->PSEL.RTS = BV_BY_NAME(UARTE_PSEL_RTS_CONNECT, Disconnected); + + // CTS + #if GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE + port = (_GPI_ARM_nRF_UART_CTS_PORT) ? NRF_P1 : NRF_P0; + port->PIN_CNF[_GPI_ARM_nRF_UART_CTS_PIN] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + #if (GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE & 2) + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + #else + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + #endif + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_UARTE0->PSEL.CTS = + BV_BY_VALUE(UARTE_PSEL_CTS_PORT, _GPI_ARM_nRF_UART_CTS_PORT) | + BV_BY_VALUE(UARTE_PSEL_CTS_PIN, _GPI_ARM_nRF_UART_CTS_PIN) | + BV_BY_NAME(UARTE_PSEL_CTS_CONNECT, Connected); + #else + NRF_UARTE0->PSEL.CTS = BV_BY_NAME(UARTE_PSEL_CTS_CONNECT, Disconnected); + #endif + } + + // set UART mode: 8 data bits, 1 stop bit, no parity + #if GPI_ARM_NRF_STDOUT_UART_FLOWCONTROL_MODE + NRF_UARTE0->CONFIG = + BV_BY_NAME(UARTE_CONFIG_HWFC, Enabled) | + BV_BY_NAME(UARTE_CONFIG_PARITY, Excluded) | + BV_BY_NAME(UARTE_CONFIG_STOP, One); + #else + NRF_UARTE0->CONFIG = + BV_BY_NAME(UARTE_CONFIG_HWFC, Disabled) | + BV_BY_NAME(UARTE_CONFIG_PARITY, Excluded) | + BV_BY_NAME(UARTE_CONFIG_STOP, One); + #endif + + // set baudrate + // Unfortunately, the documentation for the register value is very meager. + // It seems that the baudrate is generated from PCLK16M by an up-counter issuing one tick + // per overflow and the BAUDRATE register contains the increment value of that counter. + // The increment can be approximated as BAUDRATE (>)= baudrate * 2^32 / 16000000. + // It seems that the counter is 20 bit wide (i.e. only the upper bits of BAUDRATE are used). + // The following posts confirm the observations: + // https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values#post-id-1194 + // https://devzone.nordicsemi.com/f/nordic-q-a/27666/uart-baudrate-nrf52 + switch (baudrate) + { + // use official values for common baudrates + case 1200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; break; + case 2400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud2400; break; + case 4800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud4800; break; + case 9600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600; break; + case 14400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud14400; break; + case 19200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud19200; break; + case 28800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud28800; break; + case 38400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud38400; break; + case 56000: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud56000; break; + case 57600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud57600; break; + case 76800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud76800; break; + case 115200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud115200; break; + case 230400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud230400; break; + case 460800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud460800; break; + case 921600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud921600; break; + + // approximate other baudrates + // NOTE: The computation is exact for power-of-two dividers, so we do not need to + // provide explicit values for baudrates like 31250, 250000, or 1000000. + default: + { + NRF_UARTE0->BAUDRATE = ((UINT64_C(0x100000000) * baudrate) + 8000000) / 16000000; + break; + } + } + + // (un)mask interrupts + #if GPI_STDOUT_INTERRUPT_ENABLED + NRF_UARTE0->INTEN = BV_BY_NAME(UARTE_INTEN_ENDTX, Enabled); + NVIC_SetPriority(UARTE0_UART0_IRQn, GPI_ARM_NRF_STDIO_INTERRUPT_PRIORITY); + NVIC_ClearPendingIRQ(UARTE0_UART0_IRQn); + NVIC_EnableIRQ(UARTE0_UART0_IRQn); + #else + NRF_UARTE0->INTEN = 0; + #endif + + // start UART + NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Enabled); +} + +//************************************************************************************************** +//************************************************************************************************** + +#endif // __GPI_ARM_nRF528xx_PLATFORM_INTERNAL_H__ diff --git a/src/gpi/arm/nordic/nrf52840/profile.h b/src/gpi/arm/nordic/nrf528xx/profile.h similarity index 100% rename from src/gpi/arm/nordic/nrf52840/profile.h rename to src/gpi/arm/nordic/nrf528xx/profile.h diff --git a/src/gpi/arm/nordic/nrf52840/radio.c b/src/gpi/arm/nordic/nrf528xx/radio.c similarity index 85% rename from src/gpi/arm/nordic/nrf52840/radio.c rename to src/gpi/arm/nordic/nrf528xx/radio.c index a2ef6cf..a7ae1ef 100644 --- a/src/gpi/arm/nordic/nrf52840/radio.c +++ b/src/gpi/arm/nordic/nrf528xx/radio.c @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/radio.c + * @file gpi/arm/nordic/nrf528xx/radio.c * - * @brief nRF52840 radio interface + * @brief nRF528xx radio interface * - * @version $Id: 9173968a689d35b83d32dafdbba369a83a02d8aa $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -164,7 +164,25 @@ void gpi_radio_set_tx_power(unsigned int pa_level) //************************************************************************************************* -void gpi_radio_set_channel(unsigned int channel) +// directly set center frequency (alternative to gpi_radio_set_channel()) +void gpi_radio_set_center_frequency(uint_fast16_t frequency) +{ + GPI_TRACE_FUNCTION(); + + // leave >= 0.5 MHz to ISM band's boundaries, which is (rather too) little with BLE 2M + assert(frequency > 2400); + assert(frequency <= 2483); + + NRF_RADIO->FREQUENCY = + BV_BY_VALUE(RADIO_FREQUENCY_FREQUENCY, frequency - 2400) | + BV_BY_NAME(RADIO_FREQUENCY_MAP, Default); + + GPI_TRACE_RETURN(); +} + +//************************************************************************************************* + +void gpi_radio_set_channel(int channel) { GPI_TRACE_FUNCTION(); @@ -187,13 +205,42 @@ void gpi_radio_set_channel(unsigned int channel) case BLE_125k: case BLE_500k: { - assert(channel <= 39); + // ENABLE_EXTRA_CHANNELS adds three additional inofficial channels at the ISM band's + // boundaries, namely channel -1 at 2401 MHz, channel 40 at 2482 MHz, and channel 41 + // at 2483 MHz. They are interesting (at least) for internal tests because they + // promise particularly little external interference. In detail: + // + // * ISM band: 2400 ... 2483.5 + // * WiFi (>= 802.11g): 2402 ... 2482 + // * extra channel -1: 2401-w ... 2401+w no WiFi, close to BLE 0 + // * BLE RF channel 0: 2402-w ... 2402+w + // ... + // * BLE RF channel 39: 2480-w ... 2480+w + // * extra channel 40: 2482-w ... 2482+w WiFi + // * extra channel 41: 2483-w ... 2483+w no WiFi, close to ISM band boundary + // + // With a channel bandwidth of roughly 1 MHz (i.e. w = 0.5) for BLE 1M and long range + // modes the extra channels do not overlap. ATTENTION: This does not hold for BLE 2M! + // Note that BLE RF channels 0 and 39 (= BLE physical channels 37 and 39) are + // advertising channels. + #define ENABLE_EXTRA_CHANNELS 1 + + #if ENABLE_EXTRA_CHANNELS + assert((-1 <= channel) && (channel <= 41)); + #else + assert((0 <= channel) && (channel <= 39)); + #endif unsigned int freq; - // mapping of channel: see Bluetooth Core Spec. v5.1 Vol. 6 Part B section 1.4.1 + // mapping: see Bluetooth Core Spec. v5.1 Vol. 6 Part B section 1.4.1 switch (channel) { + #if ENABLE_EXTRA_CHANNELS + case -1: freq = 1; break; + case 40: freq = 82; break; + case 41: freq = 83; break; + #endif case 0 ... 10: freq = 4 + ((channel - 0) * 2); break; case 11 ... 36: freq = 28 + ((channel - 11) * 2); break; case 37: freq = 2; break; @@ -374,6 +421,7 @@ void gpi_radio_init(Gpi_Radio_Mode mode) // NRF_RADIO->DACNF = 0; // NRF_RADIO->DAB[n]/DAP[n] = 0; // don't care while DACNF = 0 // NRF_RADIO->MHRMATCH... // precise meaning of these settings is unclear + // NRF_RADIO->DFEMODE = 0; // nRF52833 gpi_radio_set_channel(37); // just as reset (default) value gpi_radio_set_tx_power(0); // just as reset (default) value diff --git a/src/gpi/arm/nordic/nrf52840/radio.h b/src/gpi/arm/nordic/nrf528xx/radio.h similarity index 91% rename from src/gpi/arm/nordic/nrf52840/radio.h rename to src/gpi/arm/nordic/nrf528xx/radio.h index 898c3ea..9612fa8 100644 --- a/src/gpi/arm/nordic/nrf52840/radio.h +++ b/src/gpi/arm/nordic/nrf528xx/radio.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/radio.h + * @file gpi/arm/nordic/nrf528xx/radio.h * - * @brief nRF52840 radio interface + * @brief nRF528xx radio interface * - * @version $Id: 2f08faa0422d6515b918a918726716463e024ed1 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,8 +45,8 @@ **************************************************************************************************/ -#ifndef __GPI_ARM_nRF52840_RADIO_H__ -#define __GPI_ARM_nRF52840_RADIO_H__ +#ifndef __GPI_ARM_nRF528xx_RADIO_H__ +#define __GPI_ARM_nRF528xx_RADIO_H__ //************************************************************************************************** //***** Includes *********************************************************************************** @@ -99,7 +99,8 @@ void gpi_radio_init(Gpi_Radio_Mode mode); Gpi_Radio_Mode gpi_radio_get_mode(); unsigned int gpi_radio_dbm_to_power_level(int dbm); void gpi_radio_set_tx_power(unsigned int pa_level); -void gpi_radio_set_channel(unsigned int channel); +void gpi_radio_set_center_frequency(uint_fast16_t frequency); +void gpi_radio_set_channel(int channel); void gpi_radio_ble_set_access_address(unsigned int address); #ifdef __cplusplus @@ -114,4 +115,4 @@ void gpi_radio_ble_set_access_address(unsigned int address); //************************************************************************************************** //************************************************************************************************** -#endif // __GPI_ARM_nRF52840_RADIO_H__ +#endif // __GPI_ARM_nRF528xx_RADIO_H__ diff --git a/src/gpi/arm/nordic/nrf52840/resource_declarations.h b/src/gpi/arm/nordic/nrf528xx/resource_declarations.h similarity index 88% rename from src/gpi/arm/nordic/nrf52840/resource_declarations.h rename to src/gpi/arm/nordic/nrf528xx/resource_declarations.h index 4c8a5d1..8f9c5de 100644 --- a/src/gpi/arm/nordic/nrf52840/resource_declarations.h +++ b/src/gpi/arm/nordic/nrf528xx/resource_declarations.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2021, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2021 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/resource_declarations.h + * @file gpi/arm/nordic/nrf528xx/resource_declarations.h * - * @brief nRF52840 resource definitions (see resource_check.h) + * @brief nRF528xx resource definitions (see resource_check.h) * - * @version $Id: d5937505a3e2f1f3d8fb7a9d0453c151876a943e $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,13 +45,13 @@ **************************************************************************************************/ -#ifndef __GPI_ARM_NRF52840_RESOURCE_DECLARATIONS_H__ -#define __GPI_ARM_NRF52840_RESOURCE_DECLARATIONS_H__ +#ifndef __GPI_ARM_NRF528xx_RESOURCE_DECLARATIONS_H__ +#define __GPI_ARM_NRF528xx_RESOURCE_DECLARATIONS_H__ //************************************************************************************************** //***** Includes *********************************************************************************** - +#include "gpi/platform_spec.h" //************************************************************************************************** //***** Global (Public) Defines and Consts ********************************************************* @@ -76,7 +76,7 @@ //************************************************************************************************** //***** Global Variables *************************************************************************** -// nRF52840 resources +// nRF528xx resources // NOTE: peripheral names are equal to those from nrf.h, besides that indexes are split up // TODO: break down subblocks as needed (gather some experience before). Consider the background @@ -106,6 +106,14 @@ GPI_RESOURCE_DECLARE(NRF_SPIM3); GPI_RESOURCE_DECLARE(NRF_NFCT); GPI_RESOURCE_DECLARE(NRF_GPIOTE); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 0); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 1); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 2); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 3); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 4); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 5); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 6); +GPI_RESOURCE_DECLARE(NRF_GPIOTE_CH, 7); GPI_RESOURCE_DECLARE(NRF_SAADC); @@ -212,6 +220,12 @@ GPI_RESOURCE_DECLARE(NRF_PPI_CH, 30); GPI_RESOURCE_DECLARE(NRF_PPI_CH, 31); GPI_RESOURCE_DECLARE(NRF_MWU); +GPI_RESOURCE_DECLARE(NRF_MWU_REGION, 0); +GPI_RESOURCE_DECLARE(NRF_MWU_REGION, 1); +GPI_RESOURCE_DECLARE(NRF_MWU_REGION, 2); +GPI_RESOURCE_DECLARE(NRF_MWU_REGION, 3); +GPI_RESOURCE_DECLARE(NRF_MWU_PREGION, 0); +GPI_RESOURCE_DECLARE(NRF_MWU_PREGION, 1); GPI_RESOURCE_DECLARE(NRF_I2S); @@ -219,9 +233,13 @@ GPI_RESOURCE_DECLARE(NRF_FPU); GPI_RESOURCE_DECLARE(NRF_USBD); -GPI_RESOURCE_DECLARE(NRF_QSPI); +#if GPI_ARCH_IS_DEVICE(nRF52840) + + GPI_RESOURCE_DECLARE(NRF_QSPI); -GPI_RESOURCE_DECLARE(NRF_CC_HOST_RGF_CRYPTOCELL); + GPI_RESOURCE_DECLARE(NRF_CC_HOST_RGF_CRYPTOCELL); + +#endif //************************************************************************************************** //***** Prototypes of Global Functions ************************************************************* @@ -244,4 +262,4 @@ GPI_RESOURCE_DECLARE(NRF_CC_HOST_RGF_CRYPTOCELL); //************************************************************************************************** //************************************************************************************************** -#endif // __GPI_ARM_NRF52840_RESOURCE_DECLARATIONS_H__ +#endif // __GPI_ARM_NRF528xx_RESOURCE_DECLARATIONS_H__ diff --git a/src/gpi/arm/nordic/nrf528xx/stdio.c b/src/gpi/arm/nordic/nrf528xx/stdio.c new file mode 100644 index 0000000..1df39bc --- /dev/null +++ b/src/gpi/arm/nordic/nrf528xx/stdio.c @@ -0,0 +1,602 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2021 - 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/nrf528xx/stdio.c + * + * @brief platform specific stdio implementation (CRT internal functions) + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ +//***** Trace Settings ***************************************************************************** + + + +//************************************************************************************************** +//**** Includes ************************************************************************************ + +#include "gpi/tools.h" +#include "gpi/platform_spec.h" +#include "gpi/platform.h" +#include "gpi/resource_check.h" + +#include + +#include + +GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); + +//************************************************************************************************** +//***** Local Defines and Consts ******************************************************************* + +// disable GPI_STDOUT_INTERRUPT_ENABLED on platforms that do not support it +#if !(GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2)) + #undef GPI_STDOUT_INTERRUPT_ENABLED + #define GPI_STDOUT_INTERRUPT_ENABLED 0 +#endif + +#define TX_CHAIN_INDEX_MASK (NUM_ELEMENTS(tx_chain) - 1) + +//************************************************************************************************** +//***** Local Typedefs and Class Declarations ****************************************************** + +#if GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2) + +// implementation of FILE structure +// for details see +struct __SEGGER_RTL_FILE_impl +{ + uint8_t stub; // only needed to enforce sizeof(FILE) > 0 +}; + +#endif + +//************************************************************************************************** +//***** Forward Declarations *********************************************************************** + + + +//************************************************************************************************** +//***** Local (Static) Variables ******************************************************************* + +// RAM area +// NOTE: do not used __RAM_segment_start__ and __RAM_segment_end__, as these symbols are +// specific for the build environment (SEGGER Embedded Studio with SEGGER Linker) +// NOTE: These symbols are used to test if some address is in RAM or not, which is a pure +// hardware decision. In other words, this is not dependent from software semantics (e.g. +// if some address range is assigned to a different software component like a bootloader). +static void * const RAM_SEGMENT_START = (void*)0x20000000; +#if (GPI_ARCH_IS_DEVICE(nRF52833)) + static void * const RAM_SEGMENT_END = (void*)0x20020000; +#elif (GPI_ARCH_IS_DEVICE(nRF52840)) + static void * const RAM_SEGMENT_END = (void*)0x20040000; +#else + #error unsupported device +#endif + +//************************************************************************************************** + +#if GPI_STDOUT_INTERRUPT_ENABLED + + ASSERT_CT_STATIC(GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS <= 32, GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS_must_not_exceed_32); + // value > 32 would require larger tx_buffer_free_map + + ASSERT_CT_STATIC(GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE < 256, GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE_must_not_exceed_255); + // tx_buffer_len is uint8_t + + static char tx_buffer[GPI_ARM_NRF_STDOUT_BUFFER_NUM_SLOTS][GPI_ARM_NRF_STDOUT_BUFFER_SLOT_SIZE]; + static uint8_t tx_buffer_len[NUM_ELEMENTS(tx_buffer)]; + static uint32_t tx_buffer_free_map = -1u >> (32 - NUM_ELEMENTS(tx_buffer)); + + static uint8_t tx_chain[1u << (MSB(NUM_ELEMENTS(tx_buffer) - 1) + 1)]; + static uint_fast32_t tx_chain_num_written = 0; + static uint_fast32_t tx_chain_num_read = 0; + + static uint_fast8_t is_tx_running = 0; + + ASSERT_CT_STATIC(IS_POWER_OF_2(NUM_ELEMENTS(tx_chain))); + +#endif + +//************************************************************************************************** + +#if GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2) + + // stdin, stdout, stderr file descriptors for UART stdio + static FILE stdin_file = { 0 }; + static FILE stdout_file = { 0 }; + static FILE stderr_file = { 0 }; + +#endif + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + +#if GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2) + + // stdin, stdout, stderr file descriptors for UART stdio + FILE *stdin = &stdin_file; + FILE *stdout = &stdout_file; + FILE *stderr = &stderr_file; + +#endif + +//************************************************************************************************** +//***** Local Functions **************************************************************************** + +#if !GPI_STDOUT_INTERRUPT_ENABLED + +// core output function +// NOTE: inline to enable optimized usage inside the adapter functions (see below) +static inline void gpi_uart_write(const void *s, unsigned int len) +{ + // test if data is in RAM (because EasyDMA cannot access flash) + #ifndef NDEBUG + ASSERT(s >= (void*)RAM_SEGMENT_START && s < (void*)RAM_SEGMENT_END); + #endif + + if (len < 1) + return; + + // flush registers before activating DMA + // this could be relevant when function gets inlined and highly optimized + REORDER_BARRIER(); + + // setup DMA + NRF_UARTE0->TXD.PTR = (uintptr_t)s; + NRF_UARTE0->TXD.MAXCNT = len; + + // flush CPU pipeline's write buffer + // NOTE: This is not really necessary here because it has been done implicitly for sure + // due to the short pipeline length of the Cortex-M4. We do it anyway to keep the code clean. + __DMB(); + + // wait until previous transmission has finished + // NOTE: TXSTARTED is used as a marker for open transmissions + if (NRF_UARTE0->EVENTS_TXSTARTED) + { + NRF_UARTE0->EVENTS_TXSTARTED = 0; + while (!(NRF_UARTE0->EVENTS_ENDTX)); + } + + // start TX + NRF_UARTE0->EVENTS_ENDTX = 0; + NRF_UARTE0->TASKS_STARTTX = 1; + + // wait until TX has been started and TXD.PTR and TXD.MAXCNT can be accessed again + // NOTE: TXD.PTR and TXD.MAXCNT are double-buffered (see spec. 4413_417 v1.2 page 511) + while (!(NRF_UARTE0->EVENTS_TXSTARTED)); +} + +#endif + +//************************************************************************************************** + +// core input function +// NOTE: inline to enable optimized usage inside the adapter functions (see below) +static inline unsigned int gpi_uart_read(void *s, unsigned int max_len) +{ + if (max_len < 1) + return max_len; + + if (max_len > 0xffff) + max_len = 0xffff; + + NRF_UARTE0->RXD.PTR = (uintptr_t)s; + NRF_UARTE0->RXD.MAXCNT = max_len; + + NRF_UARTE0->EVENTS_ENDRX = 0; + NRF_UARTE0->EVENTS_ERROR = 0; + + NRF_UARTE0->TASKS_STARTRX = 1; + + while (!(NRF_UARTE0->EVENTS_ENDRX)); + + return (NRF_UARTE0->EVENTS_ERROR) ? 0 : max_len; +} + +//************************************************************************************************** + +#if GPI_STDOUT_INTERRUPT_ENABLED + +// UART transmit ISR +void UARTE0_UART0_IRQHandler() +{ + // ATTENTION: We assume that ISR cannot be interrupted by the write routine. + + register uint_fast32_t nr = tx_chain_num_read; + register int i; + + // acknowledge IRQ + NRF_UARTE0->EVENTS_ENDTX = 0; + + // advance tx_chain_num_read, stop if done + if (++tx_chain_num_read == tx_chain_num_written) + is_tx_running = 0; + + // otherwise transmit next data block + else + { + i = tx_chain[tx_chain_num_read & TX_CHAIN_INDEX_MASK]; + NRF_UARTE0->TXD.PTR = (uintptr_t)&tx_buffer[i][0]; + NRF_UARTE0->TXD.MAXCNT = tx_buffer_len[i]; + NRF_UARTE0->TASKS_STARTTX = 1; + } + + // release completed transmit buffer slot + i = tx_chain[nr & TX_CHAIN_INDEX_MASK]; + tx_buffer_free_map |= 1u << i; +} + +#endif + +//************************************************************************************************** +//***** Global Functions *************************************************************************** + +#if GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2) + +// internal stdio functions +// provide very basic support for stdin, stdout, and stderr over UART. +// for details see . +// NOTE: functions are declared as weak to allow the application to provide different +// implementations without causing conflicts + +int __attribute__((weak)) __SEGGER_RTL_X_file_stat(FILE *stream) +{ + // stdin, stdout, and stderr are assumed to be valid + if (stream == stdin || stream == stdout || stream == stderr) + return 0; + + else return EOF; +} + +int __attribute__((weak)) __SEGGER_RTL_X_file_bufsize(FILE *stream) +{ + // avoid "variable unused" warning + (void)stream; + + return 1; +} + +int __attribute__((weak)) __SEGGER_RTL_X_file_unget(FILE *stream, int c) +{ + // avoid "variable unused" warning + (void)stream; + + // do not provide unget functionality + // this could be changed if required, + // see for an example + return EOF; +} + +#endif // GPI_ARCH_IS_OS(NONE) && GPI_ARCH_IS_CRT(SEGGER2) + +//************************************************************************************************** + +// putchar() / puts() +// ATTENTION: the simple implementations are not reentrant + +#if GPI_ARCH_IS_OS(NONE) + +#if GPI_ARCH_IS_CRT(SEGGER2) + +// Starting with SES 5.10 SEGGER included two runtime libaries: The legacy "Embedded Studio +// Runtime Library" (GPI_ARCH_CRT_SEGGER1) and the "SEGGER Runtime Library" (GPI_ARCH_CRT_SEGGER2). +// With SES 6.00 the legacy library has been removed (so SEGGER2 is the only remaining option), +// and in some version <= 6.34 the Library I/O option "STD" has also been removed. To implement +// UART-based stdio, one now has to select Library I/O option "None" and provide a set of +// __SEGGER_RTL_X_file_... functions (done here). The details can be found in +// . +// NOTE: In SES 5.xx the documentation of the RTL was inconsistent, for instance it stated that +// the function to provide for output is __SEGGER_RTL_stdout_putc(), which was not present and +// may refer to an older version of the library. + +// NOTE: function is declared as weak to allow the application to provide a different +// implementation without causing conflicts +int __attribute__((weak)) __SEGGER_RTL_X_file_write(FILE *stream, const char *s, unsigned len) +{ + if ((stream != stdout) && (stream != stderr)) + return EOF; + +// advanced interrupt driven implementation that enables higher level of async I/O +#if GPI_STDOUT_INTERRUPT_ENABLED + + const char* const end = &s[len]; + register int ie; + register int i; + + // process input data + while (s != end) + { + // NOTE: gpi_int_(un)lock() implicitly functions as a REORDER_BARRIER(), + // so we can save additional explicit barriers in the following + + #if 0 + // DEBUG: show RTS/CTS status on PCA10056 (5 = RTS, 7 = CTS(_DEFAULT)) + if (NRF_P0->IN & BV(5)) + gpi_led_on(GPI_LED_1); + else gpi_led_off(GPI_LED_1); + if (NRF_P0->IN & BV(7)) + gpi_led_on(GPI_LED_2); + else gpi_led_off(GPI_LED_2); + #endif + + // wait for a free slot in the transmit buffer pool + // ATTENTION: If is_tx_running then some slot will become free in the near future in case + // buffer is full. If !is_tx_running then number of used slots <= max. number of nested calls. + // Hence, NUM_ELEMENTS(tx_buffer) should be configured to be >= max. number of nested calls. + // Otherwise this waiting loop can cause a deadlock. + while (!tx_buffer_free_map) + REORDER_BARRIER(); + + // allocate slot in transmit buffer pool + { + ie = gpi_int_lock(); + + i = gpi_get_lsb(tx_buffer_free_map); + + // The config settings should ensure that NUM_ELEMENTS(tx_buffer) >= max. number of + // nested calls (see above). To be on the safe side, we catch exceedings with an + // infinite loop trap. We do not use assert() because assert() probably would try + // to print something out, but the print stack is the origin of the problem. + #ifndef NDEBUG + while (!((i >= 0) || is_tx_running)); + #endif + + if (i >= 0) + tx_buffer_free_map &= ~(1u << i); + + gpi_int_unlock(ie); + } + + if (i < 0) + continue; + + // copy data to buffer slot + // NOTE: as a side effect this ensures that data is placed in RAM, which is important for EasyDMA + size_t n = sizeof(tx_buffer[0]); + for (char *d = &tx_buffer[i][0]; (s != end) && (n > 0); n--) + { + if ('\n' == *s) + { + if (n < 2) + break; + + *d++ = '\r'; + n--; + } + + *d++ = *s++; + } + tx_buffer_len[i] = sizeof(tx_buffer[0]) - n; + + // push buffer slot index to transmit chain (= Tx FIFO) + { + ie = gpi_int_lock(); + + tx_chain[tx_chain_num_written++ & TX_CHAIN_INDEX_MASK] = i; + + // do not leave int lock -> if thread gets interrupted for a long time, + // transmission could finish inbetween (we would have to check tx_chain_num_read + // vs. tx_chain_num_written again, so savings in locked time would be marginal) + + // start transmitter if it is idle at present + // (otherwise transmission continues automatically, handled by the ISR) + if (!is_tx_running) + { + i = tx_chain[tx_chain_num_read & TX_CHAIN_INDEX_MASK]; + NRF_UARTE0->TXD.PTR = (uintptr_t)&tx_buffer[i][0]; + NRF_UARTE0->TXD.MAXCNT = tx_buffer_len[i]; + NRF_UARTE0->TASKS_STARTTX = 1; + is_tx_running = 1; + } + + gpi_int_unlock(ie); + } + } + +#else // GPI_STDOUT_INTERRUPT_ENABLED + + static char crlf[] = "\r\n"; // do not use const char, must be in RAM for sure + const char* const end = &s[len]; + const char *r; + unsigned int l; + + // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() + NRF_UARTE0->EVENTS_TXSTARTED = 0; + + // if data is not in RAM, we must copy it because EasyDMA has no access to flash area + if (s < (char*)RAM_SEGMENT_START || s >= (char*)RAM_SEGMENT_END) + { + char c; + for (l = len; l-- > 0;) + { + c = *s++; + if (c == '\n') + gpi_uart_write(&crlf, 2); + else gpi_uart_write(&c, 1); + } + } + + else + { + // split s into segments separated by \n + for (r = s; r != end;) + { + for (; r != end; r++) + { + if (*r == '\n') + break; + } + + // write current segment + l = (uintptr_t)r - (uintptr_t)s; + if (l > 0) + gpi_uart_write(s, l); + + // write newline sequence + if (r != end) + { + gpi_uart_write(&crlf, 2); + r++; + } + + // next segment + s = r; + } + } + + // wait until transmission has finished (so data buffer can be released for sure) + // NOTE: TXSTARTED is used as a marker for open transmissions + if (NRF_UARTE0->EVENTS_TXSTARTED) + while (!(NRF_UARTE0->EVENTS_ENDTX)); + +#endif // GPI_STDOUT_INTERRUPT_ENABLED + + // return len if successful + return len; +} + +#elif GPI_ARCH_IS_CRT(SEGGER1) + +// The (old) RTL versions call __putchar(), with an additional proprietary parameter. +// NOTE: function is declared as weak to allow the application to provide a different +// implementation (e.g. from Segger RTT) without causing conflicts +// ATTENTION: thumb_crt0.s contains a weak definition of __putchar redirecting to debug_putchar. +// Hence, to ensure that putchar redirects here it is not safe to declare __putchar alone +// (if the definition shall be weak), as this would not safely overwrite the definition from +// thumb_crt0.s. Instead we provide (non-weak) debug_putchar() (to catch thumb_crt0.s) plus +// weak __putchar(). This is somewhat dirty as debug_putchar() is meant to be the low-level +// debug output routine and should not be overwritten in general. + +int __putchar(int c, __printf_tag_ptr file) __attribute__((weak, alias("debug_putchar"))); + +int debug_putchar(int c, __printf_tag_ptr file) +{ + uint8_t buf[2]; + uint_fast8_t len = 0; + + // avoid "variable unused" warning + (void)file; + + // copy data to RAM buffer, convert "\n" to "\r\n" + if (c == '\n') + buf[len++] = '\r'; + buf[len++] = c; + + // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() + NRF_UARTE0->EVENTS_TXSTARTED = 0; + + gpi_uart_write(buf, len); + + // wait until transmission has finished + while (!(NRF_UARTE0->EVENTS_ENDTX)); + + return c; +} + +#endif // GPI_ARCH_IS_CRT(...) + +#endif // GPI_ARCH_IS_OS(NONE) + +//************************************************************************************************** +// getchar() +// ATTENTION: implementations are very simple and not reentrant + +#if GPI_ARCH_IS_OS(NONE) + +#if GPI_ARCH_IS_CRT(SEGGER2) + +// NOTE: function is declared as weak to allow the application to provide a different +// implementation without causing conflicts +int __attribute__((weak)) __SEGGER_RTL_X_file_read(FILE *stream, char *s, unsigned len) +{ + if (stdin != stream) + return EOF; + +#if 1 + len = gpi_uart_read(s, len); +#else + unsigned int l = len; + while (l) + { + unsigned int l2 = gpi_uart_read(s, l); + s += l2; + l -= l2; + } +#endif + + // return len if successful + return len; +} + +#else // GPI_ARCH_IS_CRT(SEGGER2) + +// NOTE: function is declared as weak to allow the application to provide a different +// implementation without causing conflicts +int __attribute__((weak)) getchar() +{ + uint8_t c; + + while (!gpi_uart_read(&c, 1)); + + return c; +} + +#endif // GPI_ARCH_IS_CRT(...) + +void gpi_stdin_flush() +{ + uint8_t t[8]; + + NRF_UARTE0->RXD.PTR = (uintptr_t)&t; + NRF_UARTE0->RXD.MAXCNT = 8; + + NRF_UARTE0->EVENTS_ENDRX = 0; + NRF_UARTE0->TASKS_FLUSHRX = 1; + while (!(NRF_UARTE0->EVENTS_ENDRX)); +} + +// getsn() +#include "gpi/stdio_getsn.c" + +#endif // GPI_ARCH_IS_OS(NONE) + +//************************************************************************************************** +//************************************************************************************************** diff --git a/src/gpi/arm/nordic/nrf52840/trace.h b/src/gpi/arm/nordic/nrf528xx/trace.h similarity index 88% rename from src/gpi/arm/nordic/nrf52840/trace.h rename to src/gpi/arm/nordic/nrf528xx/trace.h index fd97c2e..5c9ba3d 100644 --- a/src/gpi/arm/nordic/nrf52840/trace.h +++ b/src/gpi/arm/nordic/nrf528xx/trace.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -28,11 +28,11 @@ * ***********************************************************************************************//** * - * @file gpi/arm/nordic/nrf52840/trace.h + * @file gpi/arm/nordic/nrf528xx/trace.h * - * @brief TRACE settings for Nordic nRF52840 + * @brief TRACE settings for Nordic nRF528xx * - * @version $Id: 38fb9a19e246bb9139c1ff1d1dc90959c2aa4225 $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,14 +45,16 @@ **************************************************************************************************/ -#ifndef __GPI_nRF52840_TRACE_H__ -#define __GPI_nRF52840_TRACE_H__ +#ifndef __GPI_nRF528xx_TRACE_H__ +#define __GPI_nRF528xx_TRACE_H__ //************************************************************************************************** //***** Includes *********************************************************************************** +#include "gpi/platform_spec.h" #include "gpi/tools.h" #include "gpi/clocks.h" +#include "gpi/resource_check.h" #include @@ -110,9 +112,15 @@ #if GPI_TRACE_USE_DSR - #define GPI_TRACE_DSR_IRQ CRYPTOCELL_IRQn - #define GPI_TRACE_DSR_VECTOR CRYPTOCELL_IRQHandler - + #if GPI_ARCH_IS_DEVICE(nRF52840) + #define GPI_TRACE_DSR_IRQ CRYPTOCELL_IRQn + #define GPI_TRACE_DSR_VECTOR CRYPTOCELL_IRQHandler + #else + #define GPI_TRACE_DSR_IRQ SWI5_EGU5_IRQn + #define GPI_TRACE_DSR_VECTOR SWI5_EGU5_IRQHandler + GPI_RESOURCE_RESERVE_SHARED(NRF_EGU_SWI, 5); + #endif + static inline void gpi_trace_trigger_dsr() { NVIC->STIR = GPI_TRACE_DSR_IRQ; } #endif @@ -126,4 +134,4 @@ //************************************************************************************************** //************************************************************************************************** -#endif // __GPI_nRF52840_TRACE_H__ +#endif // __GPI_nRF528xx_TRACE_H__ diff --git a/src/gpi/arm/nordic/pca10056/clocks.h b/src/gpi/arm/nordic/pca10056/clocks.h index 296b934..f61187c 100644 --- a/src/gpi/arm/nordic/pca10056/clocks.h +++ b/src/gpi/arm/nordic/pca10056/clocks.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/clocks.h" +#include "../nrf528xx/clocks.h" diff --git a/src/gpi/arm/nordic/pca10056/gpi.c b/src/gpi/arm/nordic/pca10056/gpi.c index fd470f3..995a578 100644 --- a/src/gpi/arm/nordic/pca10056/gpi.c +++ b/src/gpi/arm/nordic/pca10056/gpi.c @@ -3,11 +3,12 @@ #include "../../armv7-m/olf.c" #include "../../armv7-m/profile.c" -#include "../nrf52840/clocks.c" -#include "../nrf52840/radio.c" +#include "../nrf528xx/platform.c" +#include "../nrf528xx/clocks.c" +#include "../nrf528xx/radio.c" +#include "../nrf528xx/stdio.c" #include "platform.c" -#include "stdio.c" #include "resource_check.c" // warn if used runtime environment has not been tested (is not explicitly supported) diff --git a/src/gpi/arm/nordic/pca10056/interrupts.h b/src/gpi/arm/nordic/pca10056/interrupts.h index 142c08a..84cc2a5 100644 --- a/src/gpi/arm/nordic/pca10056/interrupts.h +++ b/src/gpi/arm/nordic/pca10056/interrupts.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/interrupts.h" +#include "../nrf528xx/interrupts.h" diff --git a/src/gpi/arm/nordic/pca10056/olf.h b/src/gpi/arm/nordic/pca10056/olf.h index a7b61ce..7e11bbf 100644 --- a/src/gpi/arm/nordic/pca10056/olf.h +++ b/src/gpi/arm/nordic/pca10056/olf.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/olf.h" +#include "../nrf528xx/olf.h" diff --git a/src/gpi/arm/nordic/pca10056/platform.c b/src/gpi/arm/nordic/pca10056/platform.c index 3adf444..1f59b29 100644 --- a/src/gpi/arm/nordic/pca10056/platform.c +++ b/src/gpi/arm/nordic/pca10056/platform.c @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019 - 2021, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ * * @brief platform interface functions * - * @version $Id: 89cf65ff34b2484aee81c903f616c6f4570039fa $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -45,18 +45,9 @@ **************************************************************************************************/ //***** Trace Settings ***************************************************************************** -/* -#include - -// message groups for TRACE messages (used in GPI_TRACE_MSG() calls) -// define groups appropriate for your needs, assign one bit per group -// values > GPI_TRACE_LOG_USER (i.e. upper 8 bits) are reserved -#define TRACE_GROUP1 0x00000001 -#define TRACE_GROUP2 0x00000002 - -// select active message groups, i.e., the messages to be printed (others will be dropped) -GPI_TRACE_CONFIG(, TRACE_BASE_SELECTION | GPI_TRACE_LOG_USER); -*/ + + + //************************************************************************************************** //**** Includes ************************************************************************************ @@ -67,15 +58,17 @@ GPI_TRACE_CONFIG(, TRACE_BASE_SELECTION | GPI_TRACE_LOG_USER #include "gpi/clocks.h" #include "gpi/trace.h" +#include "../nrf528xx/platform_internal.h" + #include #include "gpi/resource_check.h" GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); -// main clock resources are reserved in ../nrf52840/clocks.c +// main clock resources are reserved in ../nrf528xx/clocks.c -//#if ((GPI_TRACE_MODE & GPI_TRACE_MODE_TRACE) && GPI_TRACE_USE_DSR) +//#if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) // GPI_RESOURCE_RESERVE(TODO); // GPI_TRACE_DSR_IRQ //#endif @@ -102,114 +95,12 @@ GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); //************************************************************************************************** //***** Global Variables *************************************************************************** -uint_fast8_t gpi_wakeup_event = 0; -//************************************************************************************************** -//***** Local Functions **************************************************************************** - -// init (reset) CPU core to defined state -// this function can be moved to generic ARM code if helpful -static void core_init() -{ - // NOTE: some of the regs are already initialized for sure (since program arrived here) - - gpi_int_disable(); - __set_BASEPRI(0); - __set_FAULTMASK(0); - __set_CONTROL(0); // no floating point-context, use MSP, privileged level - __DSB(); - __ISB(); - - // disable MPU - MPU->CTRL = 0; - - // TODO: enable FPU if requested - - // TODO: setup Traps and Fault Exception Handlers (if requested) - // -> regs. in System Control Block - - // TODO: setup SysTick timer if requested -} //************************************************************************************************** +//***** Local Functions **************************************************************************** -// init UART -// TODO: maybe make it a public function in platform.h (params: baudrate, flags (like HW flow control)) -// NOTE: if function is inlined and baudrate is constant then it gets well optimized -static inline void uart_init(uint32_t baudrate) -{ - assert(baudrate <= 1000000); // see spec. UARTE features - - // TODO: if enabled: STOPTX/RX - - // disable UART during reconfiguration - NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Disabled); - - // configure pins - - NRF_UARTE0->PSEL.RTS = BV_BY_NAME(UARTE_PSEL_RTS_CONNECT, Disconnected); - NRF_UARTE0->PSEL.CTS = BV_BY_NAME(UARTE_PSEL_CTS_CONNECT, Disconnected); - - NRF_UARTE0->PSEL.TXD = - BV_BY_VALUE(UARTE_PSEL_TXD_PORT, 0) | - BV_BY_VALUE(UARTE_PSEL_TXD_PIN, 6) | - BV_BY_NAME(UARTE_PSEL_TXD_CONNECT, Connected); - - NRF_UARTE0->PSEL.RXD = - BV_BY_VALUE(UARTE_PSEL_RXD_PORT, 0) | - BV_BY_VALUE(UARTE_PSEL_RXD_PIN, 8) | - BV_BY_NAME(UARTE_PSEL_RXD_CONNECT, Connected); - - // set UART mode: 8 data bits, 1 stop bit, no parity - NRF_UARTE0->CONFIG = - BV_BY_NAME(UARTE_CONFIG_HWFC, Disabled) | - BV_BY_NAME(UARTE_CONFIG_PARITY, Excluded) | - BV_BY_NAME(UARTE_CONFIG_STOP, One); - - // set baudrate - // Unfortunately, the documentation for the register value is very meager. - // It seems that the baudrate is generated from PCLK16M by an up-counter issuing one tick - // per overflow and the BAUDRATE register contains the increment value of that counter. - // The increment can be approximated as BAUDRATE (>)= baudrate * 2^32 / 16000000. - // It seems that the counter is 20 bit wide (i.e. only the upper bits of BAUDRATE are used). - // The following posts confirm the observations: - // https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values#post-id-1194 - // https://devzone.nordicsemi.com/f/nordic-q-a/27666/uart-baudrate-nrf52 - switch (baudrate) - { - // use official values for common baudrates - case 1200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; break; - case 2400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud2400; break; - case 4800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud4800; break; - case 9600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600; break; - case 14400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud14400; break; - case 19200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud19200; break; - case 28800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud28800; break; - case 38400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud38400; break; - case 56000: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud56000; break; - case 57600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud57600; break; - case 76800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud76800; break; - case 115200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud115200; break; - case 230400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud230400; break; - case 460800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud460800; break; - case 921600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud921600; break; - - // approximate other baudrates - // NOTE: The computation is exact for power-of-two dividers, so we do not need to - // provide explicit values for baudrates like 31250, 250000, or 1000000. - default: - { - NRF_UARTE0->BAUDRATE = ((UINT64_C(0x100000000) * baudrate) + 8000000) / 16000000; - break; - } - } - - // mask interrupts - NRF_UARTE0->INTEN = 0; - // start UART - NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Enabled); -} //************************************************************************************************** //***** Global Functions *************************************************************************** @@ -248,6 +139,7 @@ void gpi_platform_init() // - NFCPINS is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) // (re)init POWER settings + // supply voltage mode = High Voltage mode NRF_POWER->INTENCLR = -1u; NRF_POWER->POFCON = BV_BY_NAME(POWER_POFCON_POF, Disabled); NRF_POWER->DCDCEN = BV_BY_NAME(POWER_DCDCEN_DCDCEN, Enabled); // set REG1 to DC/DC mode @@ -255,6 +147,10 @@ void gpi_platform_init() for (i = 0; i <= 8; ++i) NRF_POWER->RAM[i].POWER = 0x0000FFFF; // all RAM sections enabled, no retention during System OFF + // enable Contant Latency mode + // for details see spec. section Sub-power modes [4413_417 v1.7 p.71] + NRF_POWER->TASKS_CONSTLAT = 1; + // disable watchdog // -> not possible if it is running already @@ -311,35 +207,21 @@ void gpi_platform_init() // P0.02 / AIN0: AREF (GPIO) // P0.03 / AIN1: A0 (GPIO) + // P0.04 / AIN2: A1 (GPIO) / CTS_OPTIONAL + // reconfigured in uart_init() if used as CTS - // P0.05 / AIN3: D16 (GPIO) / RTS - // pull RTS up to signal that flow control is not used - // (see Dev. Kit User Guide 4440_050 v1.1 section 7.2.1 for details) - NRF_P0->OUTSET = BV(5); - NRF_P0->PIN_CNF[5] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + // P0.05 / AIN3: D16 (GPIO) / RTS, used as RTS + // reconfigured in uart_init() // P0.06: D17 (GPIO) / TXD, used as TXD - NRF_P0->OUTSET = BV(6); - NRF_P0->PIN_CNF[6] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + // reconfigured in uart_init() // P0.07: D18 (GPIO) / CTS_DEFAULT / TRACECLK + // reconfigured in uart_init() if used as CTS // P0.08: D19 (GPIO) / RXD, used as RXD - NRF_P0->PIN_CNF[8] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + // reconfigured in uart_init() // P0.09 / NFC1: D20 (GPIO) / NFC // P0.10 / NFC2: D21 (GPIO) / NFC @@ -394,10 +276,6 @@ void gpi_platform_init() // P0.21: [D32 (GPIO)] / QSPI_DIO1 // P0.22: [D33 (GPIO)] / QSPI_DIO2 // P0.23: [D34 (GPIO)] / QSPI_DIO3 - // nRF52840_DK_User_Guide_v1.3 page 26: - // To use these GPIOs (incl. 0.17) for a purpose other than the onboard external - // memory and have them available on the P24 connector, six solder bridges (SB10–SB15) - // must be cut and six solder bridges (SB20–SB25) must be shorted. for (i = 19; i <= 23; ++i) { NRF_P0->PIN_CNF[i] = @@ -541,20 +419,22 @@ void gpi_platform_init() // if VHT: use PPI to connect RTC->EVENTS_TICK to TIMER->TASKS_CAPTURE #if GPI_HYBRID_CLOCK_USE_VHT - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].EEP = + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].EEP = (uintptr_t)&(_gpi_clocks_rtc->EVENTS_TICK); - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].TEP = - (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_HYBRID_CLOCK_NRF_CAPTURE_REG]); + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].TEP = + (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]); - NRF_PPI->CHENSET = BV(GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL); + NRF_PPI->CHENSET = BV(GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); #endif // init UART // ATTENTION: before using the UART HFCLK must be stable too - uart_init(115200); + // NOTE: init UART always (even if not used externally) + // because stdio functions presume it (currently) + uart_init(GPI_STDOUT_UART_BAUDRATE); // wait until clocks are stable @@ -573,7 +453,7 @@ void gpi_platform_init() // init TRACE DSR - #if ((GPI_TRACE_MODE & GPI_TRACE_MODE_TRACE) && GPI_TRACE_USE_DSR) + #if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) NVIC_SetPriority(GPI_TRACE_DSR_IRQ, 0xff); NVIC_ClearPendingIRQ(GPI_TRACE_DSR_IRQ); NVIC_EnableIRQ(GPI_TRACE_DSR_IRQ); @@ -583,102 +463,5 @@ void gpi_platform_init() // GPI_TRACE_RETURN(); } -//************************************************************************************************** - -void gpi_sleep() -{ - // disable interrupts, set PRIMASK = 1 - gpi_int_disable(); - - // mark that CPU comes from power-down - // this flag can be evaluated by the application - // NOTE: to be meaningful, the first ISR taken after power-up should clear it - gpi_wakeup_event = 1; - - // set control registers such that CPU will wake-up but not enter ISR, i.e., program returns here - // (see ARM Cortex-M4 Generic User Guide (DUI 0553A ID121610) "2.5.2 Wakeup from sleep mode" for details) - // NOTE: PRIMASK has already been set to 1 by gpi_int_disable() above -// __set_FAULTMASK(0); -// __set_PRIMASK(1); - - // enter power-down (if no IRQ pending) - // NOTE: enabled interrupts work as wake-up events even if PRIMASK = 0 - // NOTE: SCR settings are assumed to be configured by the application (fitting her needs) - __WFI(); - - // sleep... - - // restore standard behavior - // NOTE: PRIMASK = 0 reenables interrupts. In consequence, pending IRQ(s) will be taken. - __set_PRIMASK(0); -} - -//************************************************************************************************** - -void gpi_nrf_uicr_erase() -{ - // set erase-enable mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Een); - - // erase UICR - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->ERASEUICR = BV_BY_NAME(NVMC_ERASEUICR_ERASEUICR, Erase); - - // go back to read-only mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); -} - -//************************************************************************************************** - -void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size) -{ - dest = MAX(dest, sizeof(NRF_UICR->CUSTOMER)); - size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - dest); - - if (0 == size) - return; - - // set write-enable mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Wen); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - - // write UICR words - while (size > 0) - { - uint_fast8_t n = dest & 0x3; - uint32_t t; - - // assemble aligned 32-bit data word - t = 0xffffffff; - memcpy((uint8_t*)&t + n, src, MIN(4 - n, size)); - n = MIN(4 - n, size); - - // write data word - // ATTENTION: there seems to be some timing issue with READYNEXT (observed with - // optimization level 3 enabled). We add a tiny sleep period to circumvent that. - // The sleep does not hurt because writing a single word takes much more time - // anyhow (4413_417 v1.0: typ. 41us). - while (!BV_TEST_BY_NAME(NRF_NVMC->READYNEXT, NVMC_READYNEXT_READYNEXT, Ready)); - NRF_UICR->CUSTOMER[dest >> 2] = t; - gpi_micro_sleep(2); - - src = (const uint8_t*)src + n; - dest += n; - size -= n; - } - - // go back to read-only mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); -} - //************************************************************************************************** //************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10056/platform.h b/src/gpi/arm/nordic/pca10056/platform.h index 8d63fdb..298696a 100644 --- a/src/gpi/arm/nordic/pca10056/platform.h +++ b/src/gpi/arm/nordic/pca10056/platform.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2019 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,7 +32,7 @@ * * @brief platform interface functions, specific for Nordic nRF52840 DK * - * @version $Id: e5412dc587ff85a57fb38eb4108a0c4bdfe8e65b $ + * @version $Id$ * @date TODO * * @author Carsten Herrmann @@ -53,48 +53,73 @@ #include "gpi/platform_spec.h" +#include "../nrf528xx/platform.h" // nRF528xx common functionality + #include "gpi/tools.h" #include #include -#include -#include //************************************************************************************************** //***** Global (Public) Defines and Consts ********************************************************* -// ATTENTION: x is evaluated twice, so take care with side effects -#define GPI_LED(x) (((uint_fast8_t)(x) - 1) < 4 ? (BV(13) << ((uint_fast8_t)(x) - 1)) : 0) - -#define GPI_LED_NONE 0 -#define GPI_LED_1 GPI_LED(1) -#define GPI_LED_2 GPI_LED(2) -#define GPI_LED_3 GPI_LED(3) -#define GPI_LED_4 GPI_LED(4) -#define GPI_LED_5 0 - -#define GPI_BUTTON(x) x -/* -// ATTENTION: x is evaluated twice, so take care with side effects -#if 1 // DEFAULT connection - #define GPI_BUTTON(x) ((x == 1) ? 11 : ((x == 2) ? 12 : ((x == 3) ? 24 : 25))) -#else // OPTIONAL connection - #define GPI_BUTTON(x) ((x == 1) ? -7 : ((x == 2) ? -8 : ((x == 3) ? 24 : 25))) +#define GPI_LED_NONE 0 +#define GPI_LED_1 BV(13) +#define GPI_LED_2 BV(14) +#define GPI_LED_3 BV(15) +#define GPI_LED_4 BV(16) + +#if 1 // DEFAULT wiring + #define GPI_BUTTON_1 BV(11) + #define GPI_BUTTON_2 BV(12) +#else // OPTIONAL wiring + #define GPI_BUTTON_1 BV(31) | BV(7) + #define GPI_BUTTON_2 BV(31) | BV(8) #endif -*/ -//************************************************************************************************** -//***** Local (Private) Defines and Consts ********************************************************* +#define GPI_BUTTON_3 BV(24) +#define GPI_BUTTON_4 BV(25) -// bitfield macros for CMSIS register definitions +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_led_index_to_mask(int i) +{ + i -= 1; + return (i < 4) ? BV(13) << i : 0; +} -#define BV_BY_NAME(field, value) ((field ## _ ## value << field ## _Pos) & field ## _Msk) -#define BV_BY_VALUE(field, value) (((value) << field ## _Pos) & field ## _Msk) -//#define BV_BY_NAME(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) -//#define BV_BY_VALUE(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_button_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { + case 1: return GPI_BUTTON_1; + case 2: return GPI_BUTTON_2; + case 3: return GPI_BUTTON_3; + case 4: return GPI_BUTTON_4; + default: return 0; + } +} -#define BV_TEST_BY_NAME(reg, field, value) (BV_BY_NAME(field, value) == ((reg) & field ## _Msk)) -#define BV_TEST_BY_VALUE(reg, field, value) (BV_BY_VALUE(field, value) == ((reg) & field ## _Msk)) +//************************************************************************************************** +//***** Local (Private) Defines and Consts ********************************************************* + +// UART pins +#define _GPI_ARM_nRF_UART_TXD_PORT 0 +#define _GPI_ARM_nRF_UART_TXD_PIN 6 +#define _GPI_ARM_nRF_UART_RXD_PORT 0 +#define _GPI_ARM_nRF_UART_RXD_PIN 8 +#define _GPI_ARM_nRF_UART_RTS_PORT 0 +#define _GPI_ARM_nRF_UART_RTS_PIN 5 +#define _GPI_ARM_nRF_UART_CTS_PORT 0 +#if 1 // CTS = CTS_DEFAULT + #define _GPI_ARM_nRF_UART_CTS_PIN 7 +#else // CTS = CTS_OPTIONAL + #define _GPI_ARM_nRF_UART_CTS_PIN 4 +#endif //************************************************************************************************** //***** Forward Class and Struct Declarations ****************************************************** @@ -109,10 +134,7 @@ //************************************************************************************************** //***** Global Variables *************************************************************************** -// mark that CPU comes from power-down -// this flag can be evaluated by the application -// NOTE: to be meaningful, the first ISR taken after power-up should clear it -extern uint_fast8_t gpi_wakeup_event; + //************************************************************************************************** //***** Prototypes of Global Functions ************************************************************* @@ -121,21 +143,7 @@ extern uint_fast8_t gpi_wakeup_event; extern "C" { #endif -// UICR access functions -// UICR = User Information Configuration Registers, see spec. for details -// ATTENTION: Writing to UICR or flash requires NVMC->CONFIG.WEN to be set which in turn -// invalidates the instruction cache (permanently). Besides that, UICR updates take effect -// only after reset (spec. 4413_417 v1.0 4.3.3 page 24). Therefore it is highly recommended -// to do a soft reset (e.g., by calling NVIC_SystemReset()) after updating flash or UICR. -static void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size); -void gpi_nrf_uicr_erase(); -void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size); - -// standard C library does not provide getsn(), so we do it -#if GPI_ARCH_IS_OS(NONE) - void gpi_stdin_flush(); - char* getsn(char* s, size_t size); -#endif + #ifdef __cplusplus } @@ -164,44 +172,14 @@ static ALWAYS_INLINE void gpi_led_toggle(int mask) //************************************************************************************************** -static ALWAYS_INLINE uint_fast8_t gpi_button_read(int id) -{ - // NOTE: case-selection gets optimized out by constant propagation - switch (id) - { - #if 1 // DEFAULT wiring - case 1: return !(NRF_P0->IN & (1 << 11)); - case 2: return !(NRF_P0->IN & (1 << 12)); - #else // OPTIONAL wiring - case 1: return !(NRF_P1->IN & (1 << 7)); - case 2: return !(NRF_P1->IN & (1 << 8)); - #endif - case 3: return !(NRF_P0->IN & (1 << 24)); - case 4: return !(NRF_P0->IN & (1 << 25)); - default: return 0; - } - -/* typeof(NRF_P0->IN) *p; - - if (id < 0) - { - p = &(NRF_P1->IN); - id = -id; - } - else p = &(NRF_P0->IN); - - return !(*p & (1 << (id & 0x1F))); -*/ -} - -//************************************************************************************************** - -static inline void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size) +static ALWAYS_INLINE uint_fast8_t gpi_button_read(int mask) { - src = MAX(src, sizeof(NRF_UICR->CUSTOMER)); - size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - src); + // ATTENTION: we do not support multiple buttons masks + // (will lead to invalid results if buttons from P0 and P1 are mixed) - memcpy(dest, (uint8_t*)&(NRF_UICR->CUSTOMER) + src, size); + if (mask & BV(31)) + return !(NRF_P1->IN & (mask ^ BV(31))); + else return !(NRF_P0->IN & mask); } //************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10056/profile.h b/src/gpi/arm/nordic/pca10056/profile.h index 74835d4..40394a1 100644 --- a/src/gpi/arm/nordic/pca10056/profile.h +++ b/src/gpi/arm/nordic/pca10056/profile.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/profile.h" +#include "../nrf528xx/profile.h" diff --git a/src/gpi/arm/nordic/pca10056/radio.h b/src/gpi/arm/nordic/pca10056/radio.h index 71ad833..f7b1a4a 100644 --- a/src/gpi/arm/nordic/pca10056/radio.h +++ b/src/gpi/arm/nordic/pca10056/radio.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/radio.h" +#include "../nrf528xx/radio.h" diff --git a/src/gpi/arm/nordic/pca10056/resource_check.c b/src/gpi/arm/nordic/pca10056/resource_check.c index fade58f..9754b2f 100644 --- a/src/gpi/arm/nordic/pca10056/resource_check.c +++ b/src/gpi/arm/nordic/pca10056/resource_check.c @@ -3,5 +3,5 @@ // provide resource declaration symbols #if (2 == GPI_RESOURCE_CHECK_DECLARATION) - #include "../nrf52840/resource_declarations.h" + #include "../nrf528xx/resource_declarations.h" #endif diff --git a/src/gpi/arm/nordic/pca10056/resource_check.h b/src/gpi/arm/nordic/pca10056/resource_check.h index 8ab7415..5e15baf 100644 --- a/src/gpi/arm/nordic/pca10056/resource_check.h +++ b/src/gpi/arm/nordic/pca10056/resource_check.h @@ -3,5 +3,5 @@ // provide resource declaration symbols #if (2 != GPI_RESOURCE_CHECK_DECLARATION) - #include "../nrf52840/resource_declarations.h" + #include "../nrf528xx/resource_declarations.h" #endif diff --git a/src/gpi/arm/nordic/pca10056/stdio.c b/src/gpi/arm/nordic/pca10056/stdio.c deleted file mode 100644 index 985c0c9..0000000 --- a/src/gpi/arm/nordic/pca10056/stdio.c +++ /dev/null @@ -1,297 +0,0 @@ -/*************************************************************************************************** - *************************************************************************************************** - * - * Copyright (c) 2021, Networked Embedded Systems Lab, TU Dresden - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of the NES Lab or TU Dresden 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 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. - * - ***********************************************************************************************//** - * - * @file gpi/arm/nordic/pca10056/stdio.c - * - * @brief platform specific stdio implementation (CRT internal functions) - * - * @version $Id: fad7f935cfc45e964b8f964417c58c0c3ea515d6 $ - * @date TODO - * - * @author Carsten Herrmann - * - *************************************************************************************************** - - @details - - TODO - - **************************************************************************************************/ -//***** Trace Settings ***************************************************************************** - - - -//************************************************************************************************** -//**** Includes ************************************************************************************ - -#include "gpi/tools.h" -#include "gpi/platform_spec.h" -#include "gpi/platform.h" -#include "gpi/resource_check.h" - -#include - -#include - -GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); - -//************************************************************************************************** -//***** Local Defines and Consts ******************************************************************* - - - -//************************************************************************************************** -//***** Local Typedefs and Class Declarations ****************************************************** - - - -//************************************************************************************************** -//***** Forward Declarations *********************************************************************** - - - -//************************************************************************************************** -//***** Local (Static) Variables ******************************************************************* - -// RAM area -// NOTE: do not used __RAM_segment_start__ and __RAM_segment_end__, as these symbols are -// specific for the build environment (SEGGER Embedded Studio with SEGGER Linker) -static void * const RAM_SEGMENT_START = (void*)0x20000000; -static void * const RAM_SEGMENT_END = (void*)0x20040000; - -//************************************************************************************************** -//***** Global Variables *************************************************************************** - - - -//************************************************************************************************** -//***** Local Functions **************************************************************************** - -// core output function -// NOTE: inline to enable optimized usage inside the adapter functions (see below) -static inline void gpi_uart_write(const void *s, unsigned int len) -{ - // test if data is in RAM (EasyDMA cannot access flash) - ASSERT(s >= (void*)RAM_SEGMENT_START && s < (void*)RAM_SEGMENT_END); - - if (len < 1) - return; - - // flush registers before activating DMA - // this could be relevant when function gets inlined and highly optimized - REORDER_BARRIER(); - - // setup DMA - NRF_UARTE0->TXD.PTR = (uintptr_t)s; - NRF_UARTE0->TXD.MAXCNT = len; - - // flush store buffer writes from CPU pipeline - // NOTE: This is not really necessary here because it has been done implicitly for sure - // due to the short pipeline length of the Cortex-M4. We do it anyway to keep the code clean. - __DMB(); - - // wait until previous transmission has finished - // NOTE: TXSTARTED is used as a marker for open transmissions - if (NRF_UARTE0->EVENTS_TXSTARTED) - { - NRF_UARTE0->EVENTS_TXSTARTED = 0; - while (!(NRF_UARTE0->EVENTS_ENDTX)); - } - - // start TX - NRF_UARTE0->EVENTS_ENDTX = 0; - NRF_UARTE0->TASKS_STARTTX = 1; - - // wait until TX has been started and TXD.PTR and TXD.MAXCNT can be accessed again - // NOTE: TXD.PTR and TXD.MAXCNT are double-buffered (see spec. 4413_417 v1.2 page 511) - while (!(NRF_UARTE0->EVENTS_TXSTARTED)); -} - -//************************************************************************************************** -//***** Global Functions *************************************************************************** - -// putchar() / puts() -// ATTENTION: the simple implementations are not reentrant - -#if GPI_ARCH_IS_OS(NONE) - -#if GPI_ARCH_IS_CRT(SEGGER2) - -// The documentation of the SEGGER RTL says that the function to provide is __SEGGER_RTL_stdout_putc(). -// However, we have not seen it (maybe it refers to a different version of the library), and -// inspecting the code reveals that putchar(), puts() etc. call the following function -// (if Library I/O is set to STD). -int __SEGGER_RTL_X_file_write(__SEGGER_RTL_FILE *stream, const char *s, unsigned len) -{ - static char crlf[] = "\r\n"; // do not use const char, must be in RAM for sure - const char* const end = &s[len]; - const char *r; - unsigned int l; - - // avoid "variable unused" warning - (void)stream; - - // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() - NRF_UARTE0->EVENTS_TXSTARTED = 0; - - // if data is not in RAM, we must copy it because EasyDMA has no access to flash area - if (s < (char*)RAM_SEGMENT_START || s >= (char*)RAM_SEGMENT_END) - { - char c; - for (l = len; l-- > 0;) - { - c = *s++; - if (c == '\n') - gpi_uart_write(&crlf, 2); - else gpi_uart_write(&c, 1); - } - } - - else - { - // split s into segments separated by \n - for (r = s; r != end;) - { - for (; r != end; r++) - { - if (*r == '\n') - break; - } - - // write current segment - l = (uintptr_t)r - (uintptr_t)s; - if (l > 0) - gpi_uart_write(s, l); - - // write newline sequence - if (r != end) - { - gpi_uart_write(&crlf, 2); - r++; - } - - // next segment - s = r; - } - } - - // wait until transmission has finished (so data buffer can be released for sure) - // NOTE: TXSTARTED is used as a marker for open transmissions - if (NRF_UARTE0->EVENTS_TXSTARTED) - while (!(NRF_UARTE0->EVENTS_ENDTX)); - - // return value seems undocumented so far, - // we guess that it should be len or -1 in case of error - return len; -} - -#elif GPI_ARCH_IS_CRT(SEGGER1) - -// The (old) RTL versions call __putchar(), with an additional proprietary parameter. -// NOTE: function is declared as weak to allow the application to provide a different -// implementation (e.g. from Segger RTT) without causing conflicts -// ATTENTION: thumb_crt0.s contains a weak definition of __putchar redirecting to debug_putchar. -// Hence, to ensure that putchar redirects here it is not safe to declare __putchar alone -// (if the definition shall be weak), as this would not safely overwrite the definition from -// thumb_crt0.s. Instead we provide (non-weak) debug_putchar() (to catch thumb_crt0.s) plus -// weak __putchar(). This is somewhat dirty as debug_putchar() is meant to be the low-level -// debug output routine and should not be overwritten in general. - -int __putchar(int c, __printf_tag_ptr file) __attribute__((weak, alias("debug_putchar"))); - -int debug_putchar(int c, __printf_tag_ptr file) -{ - uint8_t buf[2]; - uint_fast8_t len = 0; - - // avoid "variable unused" warning - (void)file; - - // copy data to RAM buffer, convert "\n" to "\r\n" - if (c == '\n') - buf[len++] = '\r'; - buf[len++] = c; - - // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() - NRF_UARTE0->EVENTS_TXSTARTED = 0; - - gpi_uart_write(buf, len); - - // wait until transmission has finished - while (!(NRF_UARTE0->EVENTS_ENDTX)); - - return c; -} - -#endif // GPI_ARCH_IS_CRT(...) - -#endif // GPI_ARCH_IS_OS(NONE) - -//************************************************************************************************** -// getchar() -// ATTENTION: implementations are very simple and not reentrant - -#if GPI_ARCH_IS_OS(NONE) - -void gpi_stdin_flush() -{ - uint8_t t[8]; - - NRF_UARTE0->RXD.PTR = (uintptr_t)&t; - NRF_UARTE0->RXD.MAXCNT = 8; - - NRF_UARTE0->EVENTS_ENDRX = 0; - NRF_UARTE0->TASKS_FLUSHRX = 1; - while (!(NRF_UARTE0->EVENTS_ENDRX)); -} - -// NOTE: function is declared as weak to allow the application to provide a different -// implementation without causing conflicts -int __attribute__((weak)) getchar() -{ - uint8_t c; - - NRF_UARTE0->RXD.PTR = (uintptr_t)&c; - NRF_UARTE0->RXD.MAXCNT = 1; - - NRF_UARTE0->EVENTS_ENDRX = 0; - NRF_UARTE0->TASKS_STARTRX = 1; - while (!(NRF_UARTE0->EVENTS_ENDRX)); - - return c; -} - -// getsn() -#include "gpi/stdio_getsn.c" - -#endif // GPI_ARCH_IS_OS(NONE) - -//************************************************************************************************** -//************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10056/trace.h b/src/gpi/arm/nordic/pca10056/trace.h index 0795b2c..ebbf4cb 100644 --- a/src/gpi/arm/nordic/pca10056/trace.h +++ b/src/gpi/arm/nordic/pca10056/trace.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/trace.h" +#include "../nrf528xx/trace.h" diff --git a/src/gpi/arm/nordic/pca10059/clocks.h b/src/gpi/arm/nordic/pca10059/clocks.h index 296b934..f61187c 100644 --- a/src/gpi/arm/nordic/pca10059/clocks.h +++ b/src/gpi/arm/nordic/pca10059/clocks.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/clocks.h" +#include "../nrf528xx/clocks.h" diff --git a/src/gpi/arm/nordic/pca10059/gpi.c b/src/gpi/arm/nordic/pca10059/gpi.c index fd470f3..995a578 100644 --- a/src/gpi/arm/nordic/pca10059/gpi.c +++ b/src/gpi/arm/nordic/pca10059/gpi.c @@ -3,11 +3,12 @@ #include "../../armv7-m/olf.c" #include "../../armv7-m/profile.c" -#include "../nrf52840/clocks.c" -#include "../nrf52840/radio.c" +#include "../nrf528xx/platform.c" +#include "../nrf528xx/clocks.c" +#include "../nrf528xx/radio.c" +#include "../nrf528xx/stdio.c" #include "platform.c" -#include "stdio.c" #include "resource_check.c" // warn if used runtime environment has not been tested (is not explicitly supported) diff --git a/src/gpi/arm/nordic/pca10059/interrupts.h b/src/gpi/arm/nordic/pca10059/interrupts.h index 142c08a..84cc2a5 100644 --- a/src/gpi/arm/nordic/pca10059/interrupts.h +++ b/src/gpi/arm/nordic/pca10059/interrupts.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/interrupts.h" +#include "../nrf528xx/interrupts.h" diff --git a/src/gpi/arm/nordic/pca10059/olf.h b/src/gpi/arm/nordic/pca10059/olf.h index a7b61ce..7e11bbf 100644 --- a/src/gpi/arm/nordic/pca10059/olf.h +++ b/src/gpi/arm/nordic/pca10059/olf.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/olf.h" +#include "../nrf528xx/olf.h" diff --git a/src/gpi/arm/nordic/pca10059/platform.c b/src/gpi/arm/nordic/pca10059/platform.c index b959cff..458205a 100644 --- a/src/gpi/arm/nordic/pca10059/platform.c +++ b/src/gpi/arm/nordic/pca10059/platform.c @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2021, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2021 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,9 +32,10 @@ * * @brief platform interface functions * - * @version $Id: d2d9a555e5857f321458232d2618717e79420015 $ + * @version $Id$ * @date TODO * + * @author Carsten Herrmann * @author Fabian Mager * *************************************************************************************************** @@ -45,18 +46,9 @@ **************************************************************************************************/ //***** Trace Settings ***************************************************************************** -/* -#include - -// message groups for TRACE messages (used in GPI_TRACE_MSG() calls) -// define groups appropriate for your needs, assign one bit per group -// values > GPI_TRACE_LOG_USER (i.e. upper 8 bits) are reserved -#define TRACE_GROUP1 0x00000001 -#define TRACE_GROUP2 0x00000002 - -// select active message groups, i.e., the messages to be printed (others will be dropped) -GPI_TRACE_CONFIG(, TRACE_BASE_SELECTION | GPI_TRACE_LOG_USER); -*/ + + + //************************************************************************************************** //**** Includes ************************************************************************************ @@ -67,15 +59,17 @@ GPI_TRACE_CONFIG(, TRACE_BASE_SELECTION | GPI_TRACE_LOG_USER #include "gpi/clocks.h" #include "gpi/trace.h" +#include "../nrf528xx/platform_internal.h" + #include #include "gpi/resource_check.h" GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); -// main clock resources are reserved in ../nrf52840/clocks.c +// main clock resources are reserved in ../nrf528xx/clocks.c -//#if ((GPI_TRACE_MODE & GPI_TRACE_MODE_TRACE) && GPI_TRACE_USE_DSR) +//#if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) // GPI_RESOURCE_RESERVE(TODO); // GPI_TRACE_DSR_IRQ //#endif @@ -102,116 +96,12 @@ GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); //************************************************************************************************** //***** Global Variables *************************************************************************** -uint_fast8_t gpi_wakeup_event = 0; - -//************************************************************************************************** -//***** Local Functions **************************************************************************** - -// init (reset) CPU core to defined state -// this function can be moved to generic ARM code if helpful -static void core_init() -{ - // NOTE: some of the regs are already initialized for sure (since program arrived here) - - gpi_int_disable(); - __set_BASEPRI(0); - __set_FAULTMASK(0); - __set_CONTROL(0); // no floating point-context, use MSP, privileged level - __DSB(); - __ISB(); - // disable MPU - MPU->CTRL = 0; - - // TODO: enable FPU if requested - - // TODO: setup Traps and Fault Exception Handlers (if requested) - // -> regs. in System Control Block - - // TODO: setup SysTick timer if requested -} //************************************************************************************************** +//***** Local Functions **************************************************************************** -// init UART -// TODO: maybe make it a public function in platform.h (params: baudrate, flags (like HW flow control)) -// NOTE: if function is inlined and baudrate is constant then it gets well optimized -static inline void uart_init(uint32_t baudrate) -{ - assert(baudrate <= 1000000); // see spec. UARTE features - - // TODO: if enabled: STOPTX/RX - - // disable UART during reconfiguration - NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Disabled); - - // configure pins - // Current cofiguration (TXD P1.10, RXD P0.24) matches FlockLab pin mapping. - // NOTE: According to nRF52840 PS v1.1 P1.10 is only recommended for low frequency I/O. - - NRF_UARTE0->PSEL.RTS = BV_BY_NAME(UARTE_PSEL_RTS_CONNECT, Disconnected); - NRF_UARTE0->PSEL.CTS = BV_BY_NAME(UARTE_PSEL_CTS_CONNECT, Disconnected); - - NRF_UARTE0->PSEL.TXD = - BV_BY_VALUE(UARTE_PSEL_TXD_PORT, 1) | - BV_BY_VALUE(UARTE_PSEL_TXD_PIN, 10) | - BV_BY_NAME(UARTE_PSEL_TXD_CONNECT, Connected); - - NRF_UARTE0->PSEL.RXD = - BV_BY_VALUE(UARTE_PSEL_RXD_PORT, 0) | - BV_BY_VALUE(UARTE_PSEL_RXD_PIN, 24) | - BV_BY_NAME(UARTE_PSEL_RXD_CONNECT, Connected); - - // set UART mode: 8 data bits, 1 stop bit, no parity - NRF_UARTE0->CONFIG = - BV_BY_NAME(UARTE_CONFIG_HWFC, Disabled) | - BV_BY_NAME(UARTE_CONFIG_PARITY, Excluded) | - BV_BY_NAME(UARTE_CONFIG_STOP, One); - - // set baudrate - // Unfortunately, the documentation for the register value is very meager. - // It seems that the baudrate is generated from PCLK16M by an up-counter issuing one tick - // per overflow and the BAUDRATE register contains the increment value of that counter. - // The increment can be approximated as BAUDRATE (>)= baudrate * 2^32 / 16000000. - // It seems that the counter is 20 bit wide (i.e. only the upper bits of BAUDRATE are used). - // The following posts confirm the observations: - // https://devzone.nordicsemi.com/f/nordic-q-a/391/uart-baudrate-register-values#post-id-1194 - // https://devzone.nordicsemi.com/f/nordic-q-a/27666/uart-baudrate-nrf52 - switch (baudrate) - { - // use official values for common baudrates - case 1200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud1200; break; - case 2400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud2400; break; - case 4800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud4800; break; - case 9600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud9600; break; - case 14400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud14400; break; - case 19200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud19200; break; - case 28800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud28800; break; - case 38400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud38400; break; - case 56000: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud56000; break; - case 57600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud57600; break; - case 76800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud76800; break; - case 115200: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud115200; break; - case 230400: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud230400; break; - case 460800: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud460800; break; - case 921600: NRF_UARTE0->BAUDRATE = UART_BAUDRATE_BAUDRATE_Baud921600; break; - - // approximate other baudrates - // NOTE: The computation is exact for power-of-two dividers, so we do not need to - // provide explicit values for baudrates like 31250, 250000, or 1000000. - default: - { - NRF_UARTE0->BAUDRATE = ((UINT64_C(0x100000000) * baudrate) + 8000000) / 16000000; - break; - } - } - - // mask interrupts - NRF_UARTE0->INTEN = 0; - // start UART - NRF_UARTE0->ENABLE = BV_BY_NAME(UARTE_ENABLE_ENABLE, Enabled); -} //************************************************************************************************** //***** Global Functions *************************************************************************** @@ -250,6 +140,7 @@ void gpi_platform_init() // - NFCPINS is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) // (re)init POWER settings + // supply voltage mode = High Voltage mode NRF_POWER->INTENCLR = -1u; NRF_POWER->POFCON = BV_BY_NAME(POWER_POFCON_POF, Disabled); NRF_POWER->DCDCEN = BV_BY_NAME(POWER_DCDCEN_DCDCEN, Enabled); // set REG1 to DC/DC mode @@ -257,6 +148,10 @@ void gpi_platform_init() for (i = 0; i <= 8; ++i) NRF_POWER->RAM[i].POWER = 0x0000FFFF; // all RAM sections enabled, no retention during System OFF + // enable Contant Latency mode + // for details see spec. section Sub-power modes [4413_417 v1.7 p.71] + NRF_POWER->TASKS_CONSTLAT = 1; + // disable watchdog // -> not possible if it is running already @@ -300,25 +195,29 @@ void gpi_platform_init() BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); } - // Missing Port.Pin pairs are not available on this board. - // P0.00 / XL1: D2 (X2 LFXO in) + // configure used pins (others use default config from above) + // n.c. = not connected, a.s. = application specific (connected to pin header) + + // P0.00 / XL1 (D2): used as XL1 (LFXO in, connected to X2) NRF_P0->PIN_CNF[0] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.01 / XL2: F2 (X2 LFXO out) + // P0.01 / XL2 (F2): used as XL2 (LFXO out, connected to X2) NRF_P0->PIN_CNF[1] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.02 / AIN0: A12 (GPIO) - // P0.04 / AIN2: J1 (GPIO) + // P0.02 / AIN0 (A12): a.s. + // P0.03 / AIN1 (B13): n.c. + // P0.04 / AIN2 (J1) : a.s. + // P0.05 / AIN3 (K2) : n.c. - // P0.06: L1 (LED1) + // P0.06 (L1): LED1 NRF_P0->PIN_CNF[6] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | @@ -327,7 +226,9 @@ void gpi_platform_init() BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); NRF_P0->OUTSET = BV(6); // active low - // P0.08: N1 (LED2_R) + // P0.07 (M2) : n.c. + + // P0.08 (N1): LED2_R NRF_P0->PIN_CNF[8] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | @@ -336,11 +237,11 @@ void gpi_platform_init() BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); NRF_P0->OUTSET = BV(8); // active low - // P0.09 / NFC1: L24 (GPIO) - // P0.10 / NFC2: J24 (GPIO) - // P0.11 / TRACEDATA2: T2 (GPIO) + // P0.09 / NFC1 (L24): a.s. + // P0.10 / NFC2 (J24): a.s. + // P0.11 (T2): a.s. / TRACEDATA2 - // P0.12 / TRACEDATA1: U1 (LED2_B) + // P0.12 (U1): LED2_B / TRACEDATA1 NRF_P0->PIN_CNF[12] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | @@ -349,132 +250,78 @@ void gpi_platform_init() BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); NRF_P0->OUTSET = BV(12); // active low - // P0.13: AD8 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[13] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - NRF_P0->OUTCLR = BV(13); - #endif + // P0.13 (AD8) : a.s. + // P0.14 (AC9) : a.s. + // P0.15 (AD10): a.s. + // P0.16 (AC11): n.c. + // P0.17 (AD12): a.s. - // P0.14: AC9 (GPIO) - - // P0.15: AD10 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[15] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - NRF_P0->OUTCLR = BV(15); - #endif - - // P0.17: AD12 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[17] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - NRF_P0->OUTCLR = BV(17); - #endif - - // P0.18 / nRESET: AC13 (SW2 reset) + // P0.18 / nRESET (AC13): used as RESET (connected to SW2) NRF_P0->PIN_CNF[18] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.19: AC15 (SW2 reset) + // P0.19 (AC15): connected to RESET (SW2) NRF_P0->PIN_CNF[19] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.20: AD16 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[20] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - NRF_P0->OUTCLR = BV(20); - #endif + // P0.20 (AD16): a.s. - // P0.21: AC17 (SW2 reset) + // P0.21 (AC17): connected to RESET (SW2) NRF_P0->PIN_CNF[21] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.22: AD18 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[22] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - NRF_P0->OUTCLR = BV(22); - #endif + // P0.22 (AD18): a.s. - // P0.23: AC19 (SW2 reset) + // P0.23 (AC19): connected to RESET (SW2) NRF_P0->PIN_CNF[23] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.24: AD20 (GPIO) + // P0.24 (AD20): a.s. - // P0.25: AC21 (SW2 reset) + // P0.25 (AC21): connected to RESET (SW2) NRF_P0->PIN_CNF[25] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P0.26: G1 (GPIO) - // P0.29 / AIN5: A10 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[29] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - #endif - // P0.31 / AIN7: A8 (GPIO) - #if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - NRF_P0->PIN_CNF[31] = - BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | - BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | - BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | - BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - #endif - // P1.00 / TRACEDATA0: AD22 (GPIO) - // P1.01: Y23 (GPIO) - // P1.02: W24 (GPIO) - // P1.04: U24 (GPIO) - - // P1.06: R24 (SW1 button) + // P0.26 (G1): a.s. + // P0.27 (H2): n.c. + // P0.28 / AIN4 (B11): n.c. + // P0.29 / AIN5 (A10): a.s. + // P0.30 / AIN6 (B9) : n.c. + // P0.31 / AIN7 (A8) : a.s. + + // P1.00 (AD22): a.s. / TRACEDATA0 + // P1.01 (Y23) : a.s. + // P1.02 (W24) : a.s. + // P1.03 (V23) : n.c. + // P1.04 (U24) : a.s. + // P1.05 (T23) : n.c. + + // P1.06 (R24): button SW1 NRF_P1->PIN_CNF[6] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); - // P1.07: P23 (GPIO) + // P1.07 (P23): a.s. + // P1.08 (P2) : n.c. - // P1.09 / TRACEDATA3: R1 (LED2_G) + // P1.09 (R1): LED2_G / TRACEDATA3 NRF_P1->PIN_CNF[9] = BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | @@ -483,11 +330,93 @@ void gpi_platform_init() BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); NRF_P1->OUTSET = BV(9); // active low - // P1.10: A20 (GPIO) - // P1.11: B19 (GPIO) - // P1.13: A16 (GPIO) - // P1.15: A14 (GPIO) + // P1.10 (A20): a.s. + // P1.11 (B19): a.s. + // P1.12 (B17): n.c. + // P1.13 (A16): a.s. + // P1.14 (B15): n.c. (board revision 1.x.x) / GND (board revision 2.0.0) + // P1.15 (A14): a.s. + + // application specific connections of PC10059 based FlockLab target + #if GPI_ARCH_IS_BOARD(FLOCKLAB_nRF5) + + // P0.13 (AD8): LED1 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[13] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->OUTCLR = BV(13); + + // P0.15 (AD10): LED2 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[15] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->OUTCLR = BV(15); + + // P0.17 (AD12): LED3 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[17] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->OUTCLR = BV(17); + + // P0.20 (AD16): INT1 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[20] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->OUTCLR = BV(20); + + // P0.22 (AD18): INT2 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[22] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->OUTCLR = BV(22); + + // P0.24 (AD20): UART RXD + NRF_P0->PIN_CNF[24] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.29 / AIN5 (A10): SIG2 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[29] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.31 / AIN7 (A8): SIG1 (FlockLab GPIO signal name) + NRF_P0->PIN_CNF[31] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + // P1.10 (A20): UART TXD + NRF_P1->OUTSET = BV(10); + NRF_P1->PIN_CNF[10] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + #endif // GPI_ARCH_IS_BOARD(FLOCKLAB_nRF5) + // init clock system // NOTE: this should be done before initializing other peripherals @@ -575,20 +504,22 @@ void gpi_platform_init() // if VHT: use PPI to connect RTC->EVENTS_TICK to TIMER->TASKS_CAPTURE #if GPI_HYBRID_CLOCK_USE_VHT - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].EEP = + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].EEP = (uintptr_t)&(_gpi_clocks_rtc->EVENTS_TICK); - NRF_PPI->CH[GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL].TEP = - (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_HYBRID_CLOCK_NRF_CAPTURE_REG]); + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].TEP = + (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]); - NRF_PPI->CHENSET = BV(GPI_HYBRID_CLOCK_NRF_PPI_CHANNEL); + NRF_PPI->CHENSET = BV(GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); #endif // init UART // ATTENTION: before using the UART HFCLK must be stable too - uart_init(115200); + // NOTE: init UART always (even if not used externally) + // because stdio functions presume it (currently) + uart_init(GPI_STDOUT_UART_BAUDRATE); // wait until clocks are stable @@ -607,7 +538,7 @@ void gpi_platform_init() // init TRACE DSR - #if ((GPI_TRACE_MODE & GPI_TRACE_MODE_TRACE) && GPI_TRACE_USE_DSR) + #if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) NVIC_SetPriority(GPI_TRACE_DSR_IRQ, 0xff); NVIC_ClearPendingIRQ(GPI_TRACE_DSR_IRQ); NVIC_EnableIRQ(GPI_TRACE_DSR_IRQ); @@ -617,102 +548,5 @@ void gpi_platform_init() // GPI_TRACE_RETURN(); } -//************************************************************************************************** - -void gpi_sleep() -{ - // disable interrupts, set PRIMASK = 1 - gpi_int_disable(); - - // mark that CPU comes from power-down - // this flag can be evaluated by the application - // NOTE: to be meaningful, the first ISR taken after power-up should clear it - gpi_wakeup_event = 1; - - // set control registers such that CPU will wake-up but not enter ISR, i.e., program returns here - // (see ARM Cortex-M4 Generic User Guide (DUI 0553A ID121610) "2.5.2 Wakeup from sleep mode" for details) - // NOTE: PRIMASK has already been set to 1 by gpi_int_disable() above -// __set_FAULTMASK(0); -// __set_PRIMASK(1); - - // enter power-down (if no IRQ pending) - // NOTE: enabled interrupts work as wake-up events even if PRIMASK = 0 - // NOTE: SCR settings are assumed to be configured by the application (fitting her needs) - __WFI(); - - // sleep... - - // restore standard behavior - // NOTE: PRIMASK = 0 reenables interrupts. In consequence, pending IRQ(s) will be taken. - __set_PRIMASK(0); -} - -//************************************************************************************************** - -void gpi_nrf_uicr_erase() -{ - // set erase-enable mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Een); - - // erase UICR - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->ERASEUICR = BV_BY_NAME(NVMC_ERASEUICR_ERASEUICR, Erase); - - // go back to read-only mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); -} - -//************************************************************************************************** - -void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size) -{ - dest = MAX(dest, sizeof(NRF_UICR->CUSTOMER)); - size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - dest); - - if (0 == size) - return; - - // set write-enable mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Wen); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - - // write UICR words - while (size > 0) - { - uint_fast8_t n = dest & 0x3; - uint32_t t; - - // assemble aligned 32-bit data word - t = 0xffffffff; - memcpy((uint8_t*)&t + n, src, MIN(4 - n, size)); - n = MIN(4 - n, size); - - // write data word - // ATTENTION: there seems to be some timing issue with READYNEXT (observed with - // optimization level 3 enabled). We add a tiny sleep period to circumvent that. - // The sleep does not hurt because writing a single word takes much more time - // anyhow (4413_417 v1.0: typ. 41us). - while (!BV_TEST_BY_NAME(NRF_NVMC->READYNEXT, NVMC_READYNEXT_READYNEXT, Ready)); - NRF_UICR->CUSTOMER[dest >> 2] = t; - gpi_micro_sleep(2); - - src = (const uint8_t*)src + n; - dest += n; - size -= n; - } - - // go back to read-only mode - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); - NRF_NVMC->CONFIG = BV_BY_NAME(NVMC_CONFIG_WEN, Ren); - - while (!BV_TEST_BY_NAME(NRF_NVMC->READY, NVMC_READY_READY, Ready)); -} - //************************************************************************************************** //************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10059/platform.h b/src/gpi/arm/nordic/pca10059/platform.h index f9304a5..4bd65d5 100644 --- a/src/gpi/arm/nordic/pca10059/platform.h +++ b/src/gpi/arm/nordic/pca10059/platform.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2021, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2021 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -32,9 +32,10 @@ * * @brief platform interface functions, specific for Nordic nRF52840 USB Dongle * - * @version $Id: 87d7dc9305e14a04b2ff23f524e2cea356260fdd $ + * @version $Id$ * @date TODO * + * @author Carsten Herrmann * @author Fabian Mager * *************************************************************************************************** @@ -53,49 +54,108 @@ #include "gpi/platform_spec.h" +#include "../nrf528xx/platform.h" // nRF528xx common functionality + #include "gpi/tools.h" #include #include -#include -#include //************************************************************************************************** //***** Global (Public) Defines and Consts ********************************************************* -#if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) - #define GPI_LED_NONE 0 - #define GPI_LED_1 BV(13) // LED1 - #define GPI_LED_2 BV(15) // LED2 - #define GPI_LED_3 BV(17) // LED3 - #define GPI_LED_4 BV(20) // INT1 - #define GPI_LED_5 BV(22) // INT2 +#if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + + // On FlockLab, LEDs are output pins with GPIO tracing capabilities. + // Hence, we provide all such output pins as LEDs for simplicity. + #define GPI_LED_NONE 0 + #define GPI_LED_1 BV(13) // LED1 + #define GPI_LED_2 BV(15) // LED2 + #define GPI_LED_3 BV(17) // LED3 + #define GPI_LED_INT1 BV(20) // INT1 + #define GPI_LED_INT2 BV(22) // INT2 + + // following names are deprecated, use GPI_LED_INTx instead + static const int __attribute__((deprecated("use GPI_LED_INT1 instead"))) GPI_LED_4 = GPI_LED_INT1; + static const int __attribute__((deprecated("use GPI_LED_INT2 instead"))) GPI_LED_5 = GPI_LED_INT2; + + // On FlockLab, buttons are input pins with GPIO actuation capabilities. + // Hence, we provide all such input pins as buttons for simplicity. + #define GPI_BUTTON_SIG1 BV(31) // SIG1 + #define GPI_BUTTON_SIG2 BV(29) // SIG2 + #else - #define GPI_LED_NONE 0 - #define GPI_LED_1 BV(6) // P0.06 LD1 - #define GPI_LED_2 BV(8) // P0.08 LD2 (red) - #define GPI_LED_3 BV(12) // P0.12 LD2 (blue) - // TODO: gpi_led_on etc. work on port 0 so we disable GPI_LED_4 for now. - #define GPI_LED_4 0 //BV(9) // P1.09 LD2 (green) - #define GPI_LED_5 0 + + #define GPI_LED_NONE 0 + #define GPI_LED_1 BV(6) // LED1 + #define GPI_LED_2_R BV(8) // LED2 red + #define GPI_LED_2_B BV(12) // LED2 blue + #define GPI_LED_2_G BV(16+9) // LED2 green at P1.09 + + // following names are deprecated, use GPI_LED_2 instead + static const int __attribute__((deprecated("use GPI_LED_2_R instead"))) GPI_LED_2 = GPI_LED_2_R; + static const int __attribute__((deprecated("use GPI_LED_2_B instead"))) GPI_LED_3 = GPI_LED_2_B; + + #define GPI_BUTTON_1 BV(6) // SW1 + #endif -// TODO: Configure button (P1.06) -//#define GPI_BUTTON(x) x +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_led_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { +#if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + case 1: return GPI_LED_1; + case 2: return GPI_LED_2; + case 3: return GPI_LED_3; + case 4: return GPI_LED_INT1; + case 5: return GPI_LED_INT2; +#else + case 1: return GPI_LED_1; + case 2: return GPI_LED_2_R; + case 3: return GPI_LED_2_B; + case 4: return GPI_LED_2_G; +#endif + default: return 0; + } +} + +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_button_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { +#if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + case 1: return GPI_BUTTON_SIG1; + case 2: return GPI_BUTTON_SIG2; +#else + case 1: return GPI_BUTTON_1; +#endif + default: return 0; + } +} //************************************************************************************************** //***** Local (Private) Defines and Consts ********************************************************* -// bitfield macros for CMSIS register definitions - -#define BV_BY_NAME(field, value) ((field ## _ ## value << field ## _Pos) & field ## _Msk) -#define BV_BY_VALUE(field, value) (((value) << field ## _Pos) & field ## _Msk) -//#define BV_BY_NAME(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) -//#define BV_BY_VALUE(field, value) ASSERT_CT_EVAL(LSB(field ## _Msk) == field ## _Pos) - -#define BV_TEST_BY_NAME(reg, field, value) (BV_BY_NAME(field, value) == ((reg) & field ## _Msk)) -#define BV_TEST_BY_VALUE(reg, field, value) (BV_BY_VALUE(field, value) == ((reg) & field ## _Msk)) +// UART pins +#if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + #define _GPI_ARM_nRF_UART_TXD_PORT 1 + #define _GPI_ARM_nRF_UART_TXD_PIN 10 + #define _GPI_ARM_nRF_UART_RXD_PORT 0 + #define _GPI_ARM_nRF_UART_RXD_PIN 24 + // NOTE: According to nRF52840 PS v1.1, pin P1.10 is only recommended for low frequency I/O. +#endif //************************************************************************************************** //***** Forward Class and Struct Declarations ****************************************************** @@ -110,10 +170,7 @@ //************************************************************************************************** //***** Global Variables *************************************************************************** -// mark that CPU comes from power-down -// this flag can be evaluated by the application -// NOTE: to be meaningful, the first ISR taken after power-up should clear it -extern uint_fast8_t gpi_wakeup_event; + //************************************************************************************************** //***** Prototypes of Global Functions ************************************************************* @@ -122,21 +179,7 @@ extern uint_fast8_t gpi_wakeup_event; extern "C" { #endif -// UICR access functions -// UICR = User Information Configuration Registers, see spec. for details -// ATTENTION: Writing to UICR or flash requires NVMC->CONFIG.WEN to be set which in turn -// invalidates the instruction cache (permanently). Besides that, UICR updates take effect -// only after reset (spec. 4413_417 v1.0 4.3.3 page 24). Therefore it is highly recommended -// to do a soft reset (e.g., by calling NVIC_SystemReset()) after updating flash or UICR. -static void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size); -void gpi_nrf_uicr_erase(); -void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size); - -// standard C library does not provide getsn(), so we do it -#if GPI_ARCH_IS_OS(NONE) - void gpi_stdin_flush(); - char* getsn(char* s, size_t size); -#endif + #ifdef __cplusplus } @@ -144,7 +187,9 @@ void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size); //************************************************************************************************** //***** Implementations of Inline Functions ******************************************************** -#if GPI_ARCH_IS_BOARD(nRF5_FLOCKLAB) + +#if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + static ALWAYS_INLINE void gpi_led_on(int mask) { if (mask) @@ -156,67 +201,64 @@ void gpi_nrf_uicr_write(uintptr_t dest, const void *src, size_t size); if (mask) NRF_P0->OUTCLR = mask; } + + static ALWAYS_INLINE void gpi_led_toggle(int mask) + { + if (mask) + NRF_P0->OUT ^= mask; + } + #else + static ALWAYS_INLINE void gpi_led_on(int mask) { - if (mask) - NRF_P0->OUTCLR = mask; + uint_fast16_t mask0 = mask; + uint_fast16_t mask1 = mask >> 16; + + if (mask0) + NRF_P0->OUTCLR = mask0; + + if (mask1) + NRF_P1->OUTCLR = mask1; } static ALWAYS_INLINE void gpi_led_off(int mask) { - if (mask) - NRF_P0->OUTSET = mask; + uint_fast16_t mask0 = mask; + uint_fast16_t mask1 = mask >> 16; + + if (mask0) + NRF_P0->OUTSET = mask0; + + if (mask1) + NRF_P1->OUTSET = mask1; + } + + static ALWAYS_INLINE void gpi_led_toggle(int mask) + { + uint_fast16_t mask0 = mask; + uint_fast16_t mask1 = mask >> 16; + + if (mask0) + NRF_P0->OUT ^= mask0; + + if (mask1) + NRF_P1->OUT ^= mask1; } -#endif - -static ALWAYS_INLINE void gpi_led_toggle(int mask) -{ - if (mask) - NRF_P0->OUT ^= mask; -} - -//************************************************************************************************** -// TODO: Button is on port 1. -// static ALWAYS_INLINE uint_fast8_t gpi_button_read(int id) -// { -// // NOTE: case-selection gets optimized out by constant propagation -// switch (id) -// { -// #if 1 // DEFAULT wiring -// case 1: return !(NRF_P0->IN & (1 << 11)); -// case 2: return !(NRF_P0->IN & (1 << 12)); -// #else // OPTIONAL wiring -// case 1: return !(NRF_P1->IN & (1 << 7)); -// case 2: return !(NRF_P1->IN & (1 << 8)); -// #endif -// case 3: return !(NRF_P0->IN & (1 << 24)); -// case 4: return !(NRF_P0->IN & (1 << 25)); -// default: return 0; -// } - -// /* typeof(NRF_P0->IN) *p; - -// if (id < 0) -// { -// p = &(NRF_P1->IN); -// id = -id; -// } -// else p = &(NRF_P0->IN); - -// return !(*p & (1 << (id & 0x1F))); -// */ -// } +#endif //************************************************************************************************** -static inline void gpi_nrf_uicr_read(void *dest, uintptr_t src, size_t size) +static ALWAYS_INLINE uint_fast8_t gpi_button_read(int mask) { - src = MAX(src, sizeof(NRF_UICR->CUSTOMER)); - size = MAX(size, sizeof(NRF_UICR->CUSTOMER) - src); - - memcpy(dest, (uint8_t*)&(NRF_UICR->CUSTOMER) + src, size); + // NOTE: we assume that mask is valid (= a non-zero combination of GPI_BUTTON_...) + + #if GPI_ARCH_IS_BOARD(nRF_PCA10059_FLOCKLAB) + return !!(NRF_P0->IN & mask); + #else + return !(NRF_P1->IN & mask); + #endif } //************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10059/profile.h b/src/gpi/arm/nordic/pca10059/profile.h index 74835d4..40394a1 100644 --- a/src/gpi/arm/nordic/pca10059/profile.h +++ b/src/gpi/arm/nordic/pca10059/profile.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/profile.h" +#include "../nrf528xx/profile.h" diff --git a/src/gpi/arm/nordic/pca10059/radio.h b/src/gpi/arm/nordic/pca10059/radio.h index 71ad833..f7b1a4a 100644 --- a/src/gpi/arm/nordic/pca10059/radio.h +++ b/src/gpi/arm/nordic/pca10059/radio.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/radio.h" +#include "../nrf528xx/radio.h" diff --git a/src/gpi/arm/nordic/pca10059/resource_check.c b/src/gpi/arm/nordic/pca10059/resource_check.c index fade58f..9754b2f 100644 --- a/src/gpi/arm/nordic/pca10059/resource_check.c +++ b/src/gpi/arm/nordic/pca10059/resource_check.c @@ -3,5 +3,5 @@ // provide resource declaration symbols #if (2 == GPI_RESOURCE_CHECK_DECLARATION) - #include "../nrf52840/resource_declarations.h" + #include "../nrf528xx/resource_declarations.h" #endif diff --git a/src/gpi/arm/nordic/pca10059/resource_check.h b/src/gpi/arm/nordic/pca10059/resource_check.h index 8ab7415..5e15baf 100644 --- a/src/gpi/arm/nordic/pca10059/resource_check.h +++ b/src/gpi/arm/nordic/pca10059/resource_check.h @@ -3,5 +3,5 @@ // provide resource declaration symbols #if (2 != GPI_RESOURCE_CHECK_DECLARATION) - #include "../nrf52840/resource_declarations.h" + #include "../nrf528xx/resource_declarations.h" #endif diff --git a/src/gpi/arm/nordic/pca10059/stdio.c b/src/gpi/arm/nordic/pca10059/stdio.c deleted file mode 100644 index daf1261..0000000 --- a/src/gpi/arm/nordic/pca10059/stdio.c +++ /dev/null @@ -1,299 +0,0 @@ -/*************************************************************************************************** - *************************************************************************************************** - * - * Copyright (c) 2021, Networked Embedded Systems Lab, TU Dresden - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * * Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * * 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. - * * Neither the name of the NES Lab or TU Dresden 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 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. - * - ***********************************************************************************************//** - * - * @file gpi/arm/nordic/pca10059/stdio.c - * - * @brief platform specific stdio implementation (CRT internal functions) - * - * @version $Id: 890ce3187d90c15bd6ac2f83b262ecf5b3aedc43 $ - * @date TODO - * - * @author Fabian Mager - * - *************************************************************************************************** - - @details - - TODO - - **************************************************************************************************/ -//***** Trace Settings ***************************************************************************** - - - -//************************************************************************************************** -//**** Includes ************************************************************************************ - -#include "gpi/tools.h" -#include "gpi/platform_spec.h" -#include "gpi/platform.h" -#include "gpi/resource_check.h" - -#include - -#include - -GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); - -//************************************************************************************************** -//***** Local Defines and Consts ******************************************************************* - - - -//************************************************************************************************** -//***** Local Typedefs and Class Declarations ****************************************************** - - - -//************************************************************************************************** -//***** Forward Declarations *********************************************************************** - - - -//************************************************************************************************** -//***** Local (Static) Variables ******************************************************************* - -// RAM area -// NOTE: do not used __RAM_segment_start__ and __RAM_segment_end__, as these symbols are -// specific for the build environment (SEGGER Embedded Studio with SEGGER Linker) -// NOTE: MBR occupies the first flash page (address 0 - 0xFFF) and also reserves the lowest 8 bytes -// of RAM (0x20000000 - 0x20000007). -static void * const RAM_SEGMENT_START = (void*)0x20000008; -static void * const RAM_SEGMENT_END = (void*)0x20040000; - -//************************************************************************************************** -//***** Global Variables *************************************************************************** - - - -//************************************************************************************************** -//***** Local Functions **************************************************************************** - -// core output function -// NOTE: inline to enable optimized usage inside the adapter functions (see below) -static inline void gpi_uart_write(const void *s, unsigned int len) -{ - // test if data is in RAM (EasyDMA cannot access flash) - ASSERT(s >= (void*)RAM_SEGMENT_START && s < (void*)RAM_SEGMENT_END); - - if (len < 1) - return; - - // flush registers before activating DMA - // this could be relevant when function gets inlined and highly optimized - REORDER_BARRIER(); - - // setup DMA - NRF_UARTE0->TXD.PTR = (uintptr_t)s; - NRF_UARTE0->TXD.MAXCNT = len; - - // flush store buffer writes from CPU pipeline - // NOTE: This is not really necessary here because it has been done implicitly for sure - // due to the short pipeline length of the Cortex-M4. We do it anyway to keep the code clean. - __DMB(); - - // wait until previous transmission has finished - // NOTE: TXSTARTED is used as a marker for open transmissions - if (NRF_UARTE0->EVENTS_TXSTARTED) - { - NRF_UARTE0->EVENTS_TXSTARTED = 0; - while (!(NRF_UARTE0->EVENTS_ENDTX)); - } - - // start TX - NRF_UARTE0->EVENTS_ENDTX = 0; - NRF_UARTE0->TASKS_STARTTX = 1; - - // wait until TX has been started and TXD.PTR and TXD.MAXCNT can be accessed again - // NOTE: TXD.PTR and TXD.MAXCNT are double-buffered (see spec. 4413_417 v1.2 page 511) - while (!(NRF_UARTE0->EVENTS_TXSTARTED)); -} - -//************************************************************************************************** -//***** Global Functions *************************************************************************** - -// putchar() / puts() -// ATTENTION: the simple implementations are not reentrant - -#if GPI_ARCH_IS_OS(NONE) - -#if GPI_ARCH_IS_CRT(SEGGER2) - -// The documentation of the SEGGER RTL says that the function to provide is __SEGGER_RTL_stdout_putc(). -// However, we have not seen it (maybe it refers to a different version of the library), and -// inspecting the code reveals that putchar(), puts() etc. call the following function -// (if Library I/O is set to STD). -int __SEGGER_RTL_X_file_write(__SEGGER_RTL_FILE *stream, const char *s, unsigned len) -{ - static char crlf[] = "\r\n"; // do not use const char, must be in RAM for sure - const char* const end = &s[len]; - const char *r; - unsigned int l; - - // avoid "variable unused" warning - (void)stream; - - // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() - NRF_UARTE0->EVENTS_TXSTARTED = 0; - - // if data is not in RAM, we must copy it because EasyDMA has no access to flash area - if (s < (char*)RAM_SEGMENT_START || s >= (char*)RAM_SEGMENT_END) - { - char c; - for (l = len; l-- > 0;) - { - c = *s++; - if (c == '\n') - gpi_uart_write(&crlf, 2); - else gpi_uart_write(&c, 1); - } - } - - else - { - // split s into segments separated by \n - for (r = s; r != end;) - { - for (; r != end; r++) - { - if (*r == '\n') - break; - } - - // write current segment - l = (uintptr_t)r - (uintptr_t)s; - if (l > 0) - gpi_uart_write(s, l); - - // write newline sequence - if (r != end) - { - gpi_uart_write(&crlf, 2); - r++; - } - - // next segment - s = r; - } - } - - // wait until transmission has finished (so data buffer can be released for sure) - // NOTE: TXSTARTED is used as a marker for open transmissions - if (NRF_UARTE0->EVENTS_TXSTARTED) - while (!(NRF_UARTE0->EVENTS_ENDTX)); - - // return value seems undocumented so far, - // we guess that it should be len or -1 in case of error - return len; -} - -#elif GPI_ARCH_IS_CRT(SEGGER1) - -// The (old) RTL versions call __putchar(), with an additional proprietary parameter. -// NOTE: function is declared as weak to allow the application to provide a different -// implementation (e.g. from Segger RTT) without causing conflicts -// ATTENTION: thumb_crt0.s contains a weak definition of __putchar redirecting to debug_putchar. -// Hence, to ensure that putchar redirects here it is not safe to declare __putchar alone -// (if the definition shall be weak), as this would not safely overwrite the definition from -// thumb_crt0.s. Instead we provide (non-weak) debug_putchar() (to catch thumb_crt0.s) plus -// weak __putchar(). This is somewhat dirty as debug_putchar() is meant to be the low-level -// debug output routine and should not be overwritten in general. - -int __putchar(int c, __printf_tag_ptr file) __attribute__((weak, alias("debug_putchar"))); - -int debug_putchar(int c, __printf_tag_ptr file) -{ - uint8_t buf[2]; - uint_fast8_t len = 0; - - // avoid "variable unused" warning - (void)file; - - // copy data to RAM buffer, convert "\n" to "\r\n" - if (c == '\n') - buf[len++] = '\r'; - buf[len++] = c; - - // TXSTARTED is used as a marker for open transmissions in gpi_uart_write() - NRF_UARTE0->EVENTS_TXSTARTED = 0; - - gpi_uart_write(buf, len); - - // wait until transmission has finished - while (!(NRF_UARTE0->EVENTS_ENDTX)); - - return c; -} - -#endif // GPI_ARCH_IS_CRT(...) - -#endif // GPI_ARCH_IS_OS(NONE) - -//************************************************************************************************** -// getchar() -// ATTENTION: implementations are very simple and not reentrant - -#if GPI_ARCH_IS_OS(NONE) - -void gpi_stdin_flush() -{ - uint8_t t[8]; - - NRF_UARTE0->RXD.PTR = (uintptr_t)&t; - NRF_UARTE0->RXD.MAXCNT = 8; - - NRF_UARTE0->EVENTS_ENDRX = 0; - NRF_UARTE0->TASKS_FLUSHRX = 1; - while (!(NRF_UARTE0->EVENTS_ENDRX)); -} - -// NOTE: function is declared as weak to allow the application to provide a different -// implementation without causing conflicts -int __attribute__((weak)) getchar() -{ - uint8_t c; - - NRF_UARTE0->RXD.PTR = (uintptr_t)&c; - NRF_UARTE0->RXD.MAXCNT = 1; - - NRF_UARTE0->EVENTS_ENDRX = 0; - NRF_UARTE0->TASKS_STARTRX = 1; - while (!(NRF_UARTE0->EVENTS_ENDRX)); - - return c; -} - -// getsn() -#include "gpi/stdio_getsn.c" - -#endif // GPI_ARCH_IS_OS(NONE) - -//************************************************************************************************** -//************************************************************************************************** diff --git a/src/gpi/arm/nordic/pca10059/trace.h b/src/gpi/arm/nordic/pca10059/trace.h index 0795b2c..ebbf4cb 100644 --- a/src/gpi/arm/nordic/pca10059/trace.h +++ b/src/gpi/arm/nordic/pca10059/trace.h @@ -1,4 +1,4 @@ // functionality is not board specific -> provide simple wrapper -#include "../nrf52840/trace.h" +#include "../nrf528xx/trace.h" diff --git a/src/gpi/arm/nordic/riotee/clocks.h b/src/gpi/arm/nordic/riotee/clocks.h new file mode 100644 index 0000000..f61187c --- /dev/null +++ b/src/gpi/arm/nordic/riotee/clocks.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/clocks.h" diff --git a/src/gpi/arm/nordic/nrf52840/cmsis_device.h b/src/gpi/arm/nordic/riotee/cmsis_device.h similarity index 100% rename from src/gpi/arm/nordic/nrf52840/cmsis_device.h rename to src/gpi/arm/nordic/riotee/cmsis_device.h diff --git a/src/gpi/arm/nordic/riotee/gpi.c b/src/gpi/arm/nordic/riotee/gpi.c new file mode 100644 index 0000000..995a578 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/gpi.c @@ -0,0 +1,23 @@ + +#include "../../armv7-m/trace.c" +#include "../../armv7-m/olf.c" +#include "../../armv7-m/profile.c" + +#include "../nrf528xx/platform.c" +#include "../nrf528xx/clocks.c" +#include "../nrf528xx/radio.c" +#include "../nrf528xx/stdio.c" + +#include "platform.c" +#include "resource_check.c" + +// warn if used runtime environment has not been tested (is not explicitly supported) +// NOTE: This check is not strictly necessary if implementation files are written perfectly generic. +// However, the latter is unrealistic, as it is so easy to miss something (particularly regarding +// untested RTEs). Hence, we warn if an untested RTE is used. +// The check should be updated whenever a new RTE has been tested and gets officially supported afterwards. +#include "gpi/tools.h" +#include "gpi/platform_spec.h" +ASSERT_CT_WARN_STATIC( + GPI_ARCH_IS_OS(NONE) && (GPI_ARCH_IS_CRT(SEGGER1) || GPI_ARCH_IS_CRT(SEGGER2)), + untested_runtime_environment__use_GPI_at_your_own_risk); diff --git a/src/gpi/arm/nordic/riotee/interrupts.h b/src/gpi/arm/nordic/riotee/interrupts.h new file mode 100644 index 0000000..84cc2a5 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/interrupts.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/interrupts.h" diff --git a/src/gpi/arm/nordic/riotee/olf.h b/src/gpi/arm/nordic/riotee/olf.h new file mode 100644 index 0000000..7e11bbf --- /dev/null +++ b/src/gpi/arm/nordic/riotee/olf.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/olf.h" diff --git a/src/gpi/arm/nordic/riotee/platform.c b/src/gpi/arm/nordic/riotee/platform.c new file mode 100644 index 0000000..9140079 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/platform.c @@ -0,0 +1,497 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/riotee/platform.c + * + * @brief platform interface functions + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ +//***** Trace Settings ***************************************************************************** + + + +//************************************************************************************************** +//**** Includes ************************************************************************************ + +#include "gpi/tools.h" +#include "gpi/platform_spec.h" +#include "gpi/platform.h" +#include "gpi/interrupts.h" +#include "gpi/clocks.h" +#include "gpi/trace.h" + +#include "../nrf528xx/platform_internal.h" + +#include + +#include "gpi/resource_check.h" + +GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); + +// main clock resources are reserved in ../nrf528xx/clocks.c + +//#if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) +// GPI_RESOURCE_RESERVE(TODO); // GPI_TRACE_DSR_IRQ +//#endif + +//************************************************************************************************** +//***** Local Defines and Consts ******************************************************************* + +#define PCLK16_RATE 16000000u + +//************************************************************************************************** +//***** Local Typedefs and Class Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Forward Declarations *********************************************************************** + + + +//************************************************************************************************** +//***** Local (Static) Variables ******************************************************************* + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + + + +//************************************************************************************************** +//***** Local Functions **************************************************************************** + + + +//************************************************************************************************** +//***** Global Functions *************************************************************************** + +void gpi_platform_init() +{ + // GPI_TRACE_FUNCTION -> don't TRACE because HW not ready + + int i; + + // we assume bare-metal programming + // If there is an underlying OS, the init code needs to be adapted. + ASSERT_CT(GPI_ARCH_IS_OS(NONE)); + + // NOTE: We assume that gpi_platform_init() is called in the init process after a reset event, + // i.e., most of the registers are assumed to have their reset values. To perform a full system + // init from an arbitrary state, reset the device manually (e.g. use NVIC_SystemReset() from CMSIS). + + + // init (reset) CPU core to defined state + core_init(); + + // enable instruction cache + // TODO: make that configurable, disable if program runs from RAM + // NOTE: benefit of cache is application dependent + // - decreases latency on hit (0 ws.), but increases latency on miss (3 vs. 2 wait states) + // - can decrease or increase power consumption (with high and low hit rate, respectively) + // NOTE: it is possible to profile the hit rate, see spec. for details + NRF_NVMC->ICACHECNF = + BV_BY_NAME(NVMC_ICACHECNF_CACHEEN, Enabled) | + BV_BY_NAME(NVMC_ICACHECNF_CACHEPROFEN, Disabled); + + // check UICR settings + // - the settings may be application (not just board) specific -> do not touch them here + // - PSELRESET is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) + // - NFCPINS is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) + + // (re)init POWER settings + // supply voltage mode = Normal Voltage mode (VCC connected to VDD and VDDH, REG0 bypassed/disabled) + NRF_POWER->INTENCLR = -1u; + NRF_POWER->POFCON = BV_BY_NAME(POWER_POFCON_POF, Disabled); + NRF_POWER->DCDCEN = BV_BY_NAME(POWER_DCDCEN_DCDCEN, Disabled); // set REG1 to LDO mode + for (i = 0; i <= 8; ++i) + NRF_POWER->RAM[i].POWER = 0x0000FFFF; // all RAM sections enabled, no retention during System OFF + + // enable Contant Latency mode + // for details see spec. section Sub-power modes [4413_417 v1.7 p.71] + NRF_POWER->TASKS_CONSTLAT = 1; + + // disable watchdog + // -> not possible if it is running already + + // disable all PPI channels + NRF_PPI->CHEN = 0; + + + // init I/O ports + // NOTE: We do this before clock init because the latter takes some time. The goal is to + // configure all pins into a compatible state as fast as possible to avoid (or at least shorten) + // any high-current situations. + + // stop HFXO (if running), switch HFCLK to HFINT + // NOTE: by doing so, we (explicitly) ensure that the CPU runs independent from external + // signals, which is important while reconfiguring the I/O ports +// if (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_HFCLKSTOP = 1; + + // set default GPIO config: input, enable pullup or pulldown (for unconnected pins) + // NOTE: enable pullup or pulldown on unconnected pins to ensure defined logic levels + // (floating pins can increase power consumption) + // NOTE: Considering the nRF52840 specification (4413_417 v1.0 Figure 43) it is not clear + // if the pullup/down is effective / needed when the pad is disconnected (PIN_CNF.INPUT = 0). + // We enable it for sure. + // NOTE: PIN_CNF[i].DIR and DIR.PIN[i] reflect the same setting + for (i = 0; i < 32; ++i) + { + NRF_P0->PIN_CNF[i] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + NRF_P1->PIN_CNF[i] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + } + + // P0.00 / XL1: XL1 (LFXO in) + NRF_P0->PIN_CNF[0] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.01 / XL2: XL2 (LFXO out) + NRF_P0->PIN_CNF[1] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.02 / AIN0: n.c. + + // P0.03 / AIN1: LED_CTRL + // set drive mode = wired-or to avoid conflicts with MSP430 + // ATTENTION: we expect that MSP430 does the same (doesn't use push/pull) + NRF_P0->OUTCLR = BV(3); + NRF_P0->PIN_CNF[3] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, D0S1) | // wired-or + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.04 / AIN2: D2 (GPIO) + // P0.05 / AIN3: D3 (GPIO) + + // P0.06: SYS.SDA + // enable internal pull-up to ensure defined state on I2C bus + NRF_P0->PIN_CNF[6] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.07: PWRGD_H + NRF_P0->PIN_CNF[7] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.08: D1 (GPIO) + // P0.09 / NFC1: THRCTRL_H0 + // P0.10 / NFC2: n.c. + // P0.11: D7 (GPIO) + // P0.12: D10 (GPIO) + // P0.13: D8 (GPIO) + + // P0.14: C2C.MISO + // P0.15: C2C.GPIO + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[14] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->PIN_CNF[15] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.16: D9 (GPIO) + + // P0.17: C2C.MOSI + // P0.18 / RESET: C2C.CLK + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[17] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->PIN_CNF[18] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.19: n.c. + // P0.20: n.c. + // P0.21: D0 (GPIO) + + // P0.22: C2C.CS + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[22] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.23: PWRGD_L + NRF_P0->PIN_CNF[23] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.24: n.c. + + // P0.25: MAX_INT + NRF_P0->PIN_CNF[25] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.26: D5 (GPIO), Board.LED + #if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + // set drive mode = wired-or to avoid conflicts with MSP430 + // ATTENTION: we expect that MSP430 does the same (doesn't use push/pull) + NRF_P0->OUTCLR = BV(26); + NRF_P0->PIN_CNF[26] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, D0S1) | // wired-or + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + #endif + + // P0.27: n.c. + // P0.28 / AIN4: n.c. + + // P0.29 / AIN5: VCAP_SENSE + NRF_P0->PIN_CNF[29] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.30 / AIN6: RTC_INT + NRF_P0->PIN_CNF[30] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.31 / AIN7: n.c. + // P1.00: n.c. + // P1.01: n.c. + // P1.02: THRCTRL.H1 + + // P1.03: D6 (GPIO), Board.BUTTON + #if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + NRF_P1->PIN_CNF[3] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + #endif + + // P1.04: THRCTRL.L1 + // P1.05: n.c. + // P1.06: n.c. + // P1.07: THRCTRL.L0 + + // P1.08: SYS.SCL + // enable internal pull-up to ensure defined state on I2C bus + NRF_P1->PIN_CNF[8] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P1.09: D4 (GPIO) + + + // init clock system + // NOTE: this should be done before initializing other peripherals + + // setup: + // HFCLK: 64MHz from XO (XO running with 32MHz) + // LFCLK: 32768Hz from XO + // TIMERx (fast clock): 16MHz from PCLK16M, 32-bit timer + // RTCx (slow clock): 32768Hz from LFCLK + + // ATTENTION: the CPU is running, so some valid configuration is already there. When we change + // the settings, we must take care that the CPU keeps running for sure after every single step. + // Hence, the order of changes has to be designed with care. + + // stop HFXO (if running), implicitly switch HFCLK to HFINT +// if (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_HFCLKSTOP = 1; + + // stop LFXO (if running) +// if (NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_LFCLKSTOP = 1; + + // init CLOCK module + NRF_CLOCK->INTENCLR = -1u; + NRF_CLOCK->LFCLKSRC = + BV_BY_NAME(CLOCK_LFCLKSRC_SRC, Xtal) | + BV_BY_NAME(CLOCK_LFCLKSRC_BYPASS, Disabled) | + BV_BY_NAME(CLOCK_LFCLKSRC_EXTERNAL, Disabled); + NRF_CLOCK->HFXODEBOUNCE = + BV_BY_VALUE(CLOCK_HFXODEBOUNCE_HFXODEBOUNCE, 0x40); + NRF_CLOCK->LFXODEBOUNCE = + BV_BY_NAME(CLOCK_LFXODEBOUNCE_LFXODEBOUNCE, Normal); + // TODO: set to Extended if extended temperature range is needed [4452_021 v1.6 p.94] + + // start HFXO + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + NRF_CLOCK->TASKS_HFCLKSTART = 1; + + // start LFXO + NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK->TASKS_LFCLKSTART = 1; + + // init fast-clock timer + { + _gpi_clocks_fast_timer->TASKS_STOP = 1; + + // wait a bit to make sure that STOP gets processed + // (see spec. 4413_417 v1.0 page 422 "Task delays") + // ATTENTION: since we stopped the timer, we cannot use it to measure time. + // Therefore we use some nops here. + for (i = 4; i-- > 0;) + __NOP(); + + _gpi_clocks_fast_timer->SHORTS = 0; + _gpi_clocks_fast_timer->INTENCLR = -1u; + _gpi_clocks_fast_timer->MODE = BV_BY_NAME(TIMER_MODE_MODE, Timer); + _gpi_clocks_fast_timer->BITMODE = BV_BY_NAME(TIMER_BITMODE_BITMODE, 32Bit); + + ASSERT_CT(PCLK16_RATE == (PCLK16_RATE / GPI_FAST_CLOCK_RATE) * GPI_FAST_CLOCK_RATE, GPI_FAST_CLOCK_RATE_invalid); + ASSERT_CT(IS_POWER_OF_2(PCLK16_RATE / GPI_FAST_CLOCK_RATE), GPI_FAST_CLOCK_RATE_invalid); + ASSERT_CT(MSB(PCLK16_RATE / GPI_FAST_CLOCK_RATE) <= 9, GPI_FAST_CLOCK_RATE_invalid); + + _gpi_clocks_fast_timer->PRESCALER = + BV_BY_VALUE(TIMER_PRESCALER_PRESCALER, MSB(PCLK16_RATE / GPI_FAST_CLOCK_RATE)); + + _gpi_clocks_fast_timer->TASKS_START = 1; + } + + // init RTC (slow clock) + { + _gpi_clocks_rtc->TASKS_STOP = 1; + + // NOTE: The spec. does not mention anything that we have to wait a bit after STOP. + // If there are any troubles, check again. + + _gpi_clocks_rtc->INTENCLR = -1u; + _gpi_clocks_rtc->EVTEN = BV_BY_VALUE(RTC_EVTENSET_TICK, 1); + // EVENTS_TICK is needed for ready-polling below and (optionally) VHT + + ASSERT_CT(32768 == (32768 / GPI_SLOW_CLOCK_RATE) * GPI_SLOW_CLOCK_RATE, GPI_SLOW_CLOCK_RATE_invalid); + ASSERT_CT((32768 / GPI_SLOW_CLOCK_RATE) <= 0x1000, GPI_SLOW_CLOCK_RATE_too_low); + + _gpi_clocks_rtc->PRESCALER = (32768 / GPI_SLOW_CLOCK_RATE) - 1; + + _gpi_clocks_rtc->TASKS_START = 1; + } + + // if VHT: use PPI to connect RTC->EVENTS_TICK to TIMER->TASKS_CAPTURE + #if GPI_HYBRID_CLOCK_USE_VHT + + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].EEP = + (uintptr_t)&(_gpi_clocks_rtc->EVENTS_TICK); + + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].TEP = + (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]); + + NRF_PPI->CHENSET = BV(GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); + + #endif + + + // init UART + // ATTENTION: before using the UART HFCLK must be stable too + // NOTE: init UART always (even if not used externally) + // because stdio functions presume it (currently) + uart_init(GPI_STDOUT_UART_BAUDRATE); + + + // wait until clocks are stable + // ATTENTION: The clocks are not reliable before. However, we can interleave all init tasks + // that do not rely on clock accuracy. + // ATTENTION: Since LFCLK may have been unstable when we initialized the RTC (see above), + // we wait until RTC started up for sure (see spec. 4413_417 v1.0 page 339 START task delay) + while (!(NRF_CLOCK->EVENTS_HFCLKSTARTED && NRF_CLOCK->EVENTS_LFCLKSTARTED)); + _gpi_clocks_rtc->EVENTS_TICK = 0; + while (!(_gpi_clocks_rtc->EVENTS_TICK)); + + // if !VHT: disable RTC->EVENTS_TICK (not needed anymore) + #if !GPI_HYBRID_CLOCK_USE_VHT + _gpi_clocks_rtc->EVTENCLR = BV_BY_VALUE(RTC_EVTENSET_TICK, 1); + #endif + + + // init TRACE DSR + #if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) + NVIC_SetPriority(GPI_TRACE_DSR_IRQ, 0xff); + NVIC_ClearPendingIRQ(GPI_TRACE_DSR_IRQ); + NVIC_EnableIRQ(GPI_TRACE_DSR_IRQ); + #endif + + + // GPI_TRACE_RETURN(); +} + +//************************************************************************************************** +//************************************************************************************************** diff --git a/src/gpi/arm/nordic/riotee/platform.h b/src/gpi/arm/nordic/riotee/platform.h new file mode 100644 index 0000000..c810726 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/platform.h @@ -0,0 +1,218 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2024, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/riotee/platform.h + * + * @brief platform interface functions, specific for Nessie Circuit's Riotee + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ + +#ifndef __GPI_ARM_nRF_RIOTEE_PLATFORM_H__ +#define __GPI_ARM_nRF_RIOTEE_PLATFORM_H__ + +//************************************************************************************************** +//***** Includes *********************************************************************************** + +#include "gpi/platform_spec.h" + +#include "../nrf528xx/platform.h" // nRF528xx common functionality + +#include "gpi/tools.h" + +#include + +#include + +//************************************************************************************************** +//***** Global (Public) Defines and Consts ********************************************************* + +#define GPI_LED_NONE 0 +#define GPI_LED_1 BV(3) + +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + #define GPI_LED_BOARD_1 BV(26) +#endif + +#define GPI_BUTTON_PWRGD_H BV(7) +#define GPI_BUTTON_PWRGD_L BV(23) +#define GPI_BUTTON_MAX_INT BV(25) +#define GPI_BUTTON_RTC_INT BV(30) + +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + #define GPI_BUTTON_BOARD_1 BV(3) +#endif + +// internal mapping to P0 and P1 +// ATTENTION: concept works as long as bit positions at P0 and P1 do not overlap. +// If they do, shift bit mapping at P0 or P1 by a constant to find a non-overlapping combination. +// If impossible, there is more work to do. +// for details see gpi_button_read() +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + #define _GPI_BUTTON_P0_MASK (GPI_BUTTON_PWRGD_H | GPI_BUTTON_PWRGD_L | GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT) + #define _GPI_BUTTON_P1_MASK (GPI_BUTTON_BOARD_1) + #define _GPI_BUTTON_INV_MASK (GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT | GPI_BUTTON_BOARD_1) +#else + #define _GPI_BUTTON_P0_MASK (GPI_BUTTON_PWRGD_H | GPI_BUTTON_PWRGD_L | GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT) + #define _GPI_BUTTON_P1_MASK 0 + #define _GPI_BUTTON_INV_MASK (GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT) +#endif + +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_led_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { + case 1: return GPI_LED_1; + +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + case 2: return GPI_LED_BOARD_1; +#endif + default: return GPI_LED_NONE; + } +} + +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_button_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { + case 1: return GPI_BUTTON_PWRGD_H; + case 2: return GPI_BUTTON_PWRGD_L; + case 3: return GPI_BUTTON_MAX_INT; + case 4: return GPI_BUTTON_RTC_INT; + +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + case 5: return GPI_BUTTON_BOARD_1; +#endif + default: return 0; + } +} + +//************************************************************************************************** +//***** Local (Private) Defines and Consts ********************************************************* + +// UART pins +#if GPI_ARCH_IS_BOARD(NESSIE_RIOTEE_NRF_BOARD) + #define _GPI_ARM_nRF_UART_TXD_PORT 0 + #define _GPI_ARM_nRF_UART_TXD_PIN 8 + #define _GPI_ARM_nRF_UART_RXD_PORT 0 + #define _GPI_ARM_nRF_UART_RXD_PIN 21 +#endif + +//************************************************************************************************** +//***** Forward Class and Struct Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Global Typedefs and Class Declarations ***************************************************** + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + + + +//************************************************************************************************** +//***** Prototypes of Global Functions ************************************************************* + +#ifdef __cplusplus + extern "C" { +#endif + + + +#ifdef __cplusplus + } +#endif + +//************************************************************************************************** +//***** Implementations of Inline Functions ******************************************************** + +static ALWAYS_INLINE void gpi_led_on(int mask) +{ + if (mask) + NRF_P0->OUTSET = mask; +} + +static ALWAYS_INLINE void gpi_led_off(int mask) +{ + if (mask) + NRF_P0->OUTCLR = mask; +} + +static ALWAYS_INLINE void gpi_led_toggle(int mask) +{ + if (mask) + NRF_P0->OUT ^= mask; +} + +//************************************************************************************************** + +static ALWAYS_INLINE uint_fast32_t gpi_button_read(int mask) +{ + uint_fast32_t x = 0; + + if (mask & _GPI_BUTTON_P0_MASK) + x |= NRF_P0->IN & _GPI_BUTTON_P0_MASK; + + if (mask & _GPI_BUTTON_P1_MASK) + x |= NRF_P1->IN & _GPI_BUTTON_P1_MASK; + + x ^= _GPI_BUTTON_INV_MASK; + x &= mask; + + return x; +} + +//************************************************************************************************** +//************************************************************************************************** + +#endif // __GPI_ARM_nRF_RIOTEE_PLATFORM_H__ diff --git a/src/gpi/arm/nordic/riotee/profile.h b/src/gpi/arm/nordic/riotee/profile.h new file mode 100644 index 0000000..40394a1 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/profile.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/profile.h" diff --git a/src/gpi/arm/nordic/riotee/radio.h b/src/gpi/arm/nordic/riotee/radio.h new file mode 100644 index 0000000..f7b1a4a --- /dev/null +++ b/src/gpi/arm/nordic/riotee/radio.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/radio.h" diff --git a/src/gpi/arm/nordic/riotee/resource_check.c b/src/gpi/arm/nordic/riotee/resource_check.c new file mode 100644 index 0000000..9754b2f --- /dev/null +++ b/src/gpi/arm/nordic/riotee/resource_check.c @@ -0,0 +1,7 @@ + +#include "gpi/resource_check.h" + +// provide resource declaration symbols +#if (2 == GPI_RESOURCE_CHECK_DECLARATION) + #include "../nrf528xx/resource_declarations.h" +#endif diff --git a/src/gpi/arm/nordic/riotee/resource_check.h b/src/gpi/arm/nordic/riotee/resource_check.h new file mode 100644 index 0000000..5e15baf --- /dev/null +++ b/src/gpi/arm/nordic/riotee/resource_check.h @@ -0,0 +1,7 @@ + +// do not #include "gpi/resource_check.h" because order is vice versa (current file is included there) + +// provide resource declaration symbols +#if (2 != GPI_RESOURCE_CHECK_DECLARATION) + #include "../nrf528xx/resource_declarations.h" +#endif diff --git a/src/gpi/arm/nordic/riotee/resource_check_pre_include.h b/src/gpi/arm/nordic/riotee/resource_check_pre_include.h new file mode 100644 index 0000000..959b469 --- /dev/null +++ b/src/gpi/arm/nordic/riotee/resource_check_pre_include.h @@ -0,0 +1,3 @@ + +// nothing to do here + diff --git a/src/gpi/arm/nordic/riotee/trace.h b/src/gpi/arm/nordic/riotee/trace.h new file mode 100644 index 0000000..ebbf4cb --- /dev/null +++ b/src/gpi/arm/nordic/riotee/trace.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/trace.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/clocks.h b/src/gpi/arm/nordic/shepherd_nrf52/clocks.h new file mode 100644 index 0000000..f61187c --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/clocks.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/clocks.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/cmsis_device.h b/src/gpi/arm/nordic/shepherd_nrf52/cmsis_device.h new file mode 100644 index 0000000..1cacedf --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/cmsis_device.h @@ -0,0 +1,4 @@ + +// redirect to corresponding CMSIS file + +#include diff --git a/src/gpi/arm/nordic/shepherd_nrf52/gpi.c b/src/gpi/arm/nordic/shepherd_nrf52/gpi.c new file mode 100644 index 0000000..995a578 --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/gpi.c @@ -0,0 +1,23 @@ + +#include "../../armv7-m/trace.c" +#include "../../armv7-m/olf.c" +#include "../../armv7-m/profile.c" + +#include "../nrf528xx/platform.c" +#include "../nrf528xx/clocks.c" +#include "../nrf528xx/radio.c" +#include "../nrf528xx/stdio.c" + +#include "platform.c" +#include "resource_check.c" + +// warn if used runtime environment has not been tested (is not explicitly supported) +// NOTE: This check is not strictly necessary if implementation files are written perfectly generic. +// However, the latter is unrealistic, as it is so easy to miss something (particularly regarding +// untested RTEs). Hence, we warn if an untested RTE is used. +// The check should be updated whenever a new RTE has been tested and gets officially supported afterwards. +#include "gpi/tools.h" +#include "gpi/platform_spec.h" +ASSERT_CT_WARN_STATIC( + GPI_ARCH_IS_OS(NONE) && (GPI_ARCH_IS_CRT(SEGGER1) || GPI_ARCH_IS_CRT(SEGGER2)), + untested_runtime_environment__use_GPI_at_your_own_risk); diff --git a/src/gpi/arm/nordic/shepherd_nrf52/interrupts.h b/src/gpi/arm/nordic/shepherd_nrf52/interrupts.h new file mode 100644 index 0000000..84cc2a5 --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/interrupts.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/interrupts.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/olf.h b/src/gpi/arm/nordic/shepherd_nrf52/olf.h new file mode 100644 index 0000000..7e11bbf --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/olf.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/olf.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/platform.c b/src/gpi/arm/nordic/shepherd_nrf52/platform.c new file mode 100644 index 0000000..08f36cb --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/platform.c @@ -0,0 +1,608 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2025, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/shepherd_nrf52/platform.c + * + * @brief platform interface functions + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ +//***** Trace Settings ***************************************************************************** + + + +//************************************************************************************************** +//**** Includes ************************************************************************************ + +#include "gpi/tools.h" +#include "gpi/platform_spec.h" +#include "gpi/platform.h" +#include "gpi/interrupts.h" +#include "gpi/clocks.h" +#include "gpi/trace.h" + +#include "../nrf528xx/platform_internal.h" + +#include + +#include "gpi/resource_check.h" + +GPI_RESOURCE_RESERVE_SHARED(NRF_UARTE, 0); + +// main clock resources are reserved in ../nrf528xx/clocks.c + +//#if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) +// GPI_RESOURCE_RESERVE(TODO); // GPI_TRACE_DSR_IRQ +//#endif + +//************************************************************************************************** +//***** Local Defines and Consts ******************************************************************* + +#define PCLK16_RATE 16000000u + +//************************************************************************************************** +//***** Local Typedefs and Class Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Forward Declarations *********************************************************************** + + + +//************************************************************************************************** +//***** Local (Static) Variables ******************************************************************* + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + + + +//************************************************************************************************** +//***** Local Functions **************************************************************************** + + + +//************************************************************************************************** +//***** Global Functions *************************************************************************** + +void gpi_platform_init() +{ + // GPI_TRACE_FUNCTION -> don't TRACE because HW not ready + + int i; + + // we assume bare-metal programming + // If there is an underlying OS, the init code needs to be adapted. + ASSERT_CT(GPI_ARCH_IS_OS(NONE)); + + // NOTE: We assume that gpi_platform_init() is called in the init process after a reset event, + // i.e., most of the registers are assumed to have their reset values. To perform a full system + // init from an arbitrary state, reset the device manually (e.g. use NVIC_SystemReset() from CMSIS). + + + // init (reset) CPU core to defined state + core_init(); + + // enable instruction cache + // TODO: make that configurable, disable if program runs from RAM + // NOTE: benefit of cache is application dependent + // - decreases latency on hit (0 ws.), but increases latency on miss (3 vs. 2 wait states) + // - can decrease or increase power consumption (with high and low hit rate, respectively) + // NOTE: it is possible to profile the hit rate, see spec. for details + NRF_NVMC->ICACHECNF = + BV_BY_NAME(NVMC_ICACHECNF_CACHEEN, Enabled) | + BV_BY_NAME(NVMC_ICACHECNF_CACHEPROFEN, Disabled); + + // check UICR settings + // - the settings may be application (not just board) specific -> do not touch them here + // - PSELRESET is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) + // - NFCPINS is handled in nRF startup code (see CONFIG_NFCT_PINS_AS_GPIOS) + + // (re)init POWER settings + // supply voltage mode = Normal Voltage mode (VCC connected to VDD and VDDH, REG0 bypassed/disabled) + // NOTE: there are no external LC filters connected, so DC/DC mode cannot be used (board rev. 1.3) + NRF_POWER->INTENCLR = -1u; + NRF_POWER->POFCON = BV_BY_NAME(POWER_POFCON_POF, Disabled); + NRF_POWER->DCDCEN = BV_BY_NAME(POWER_DCDCEN_DCDCEN, Disabled); // set REG1 to LDO mode + NRF_POWER->DCDCEN0 = BV_BY_NAME(POWER_DCDCEN0_DCDCEN, Disabled); // set REG0 to LDO mode (don't care because bypassed) + for (i = 0; i <= 8; ++i) + NRF_POWER->RAM[i].POWER = 0x0000FFFF; // all RAM sections enabled, no retention during System OFF + + // enable Contant Latency mode + // for details see spec. section Sub-power modes [4413_417 v1.7 p.71] + NRF_POWER->TASKS_CONSTLAT = 1; + + // disable watchdog + // -> not possible if it is running already + + // disable all PPI channels + NRF_PPI->CHEN = 0; + + + // init I/O ports + // NOTE: We do this before clock init because the latter takes some time. The goal is to + // configure all pins into a compatible state as fast as possible to avoid (or at least shorten) + // any high-current situations. + + // stop HFXO (if running), switch HFCLK to HFINT + // NOTE: by doing so, we (explicitly) ensure that the CPU runs independent from external + // signals, which is important while reconfiguring the I/O ports +// if (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_HFCLKSTOP = 1; + + // set default GPIO config: input, enable pullup or pulldown (for unconnected pins) + // NOTE: enable pullup or pulldown on unconnected pins to ensure defined logic levels + // (floating pins can increase power consumption) + // NOTE: Considering the nRF52840 specification (4413_417 v1.0 Figure 43) it is not clear + // if the pullup/down is effective / needed when the pad is disconnected (PIN_CNF.INPUT = 0). + // We enable it for sure. + // NOTE: PIN_CNF[i].DIR and DIR.PIN[i] reflect the same setting + for (i = 0; i < 32; ++i) + { + NRF_P0->PIN_CNF[i] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + NRF_P1->PIN_CNF[i] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + } + + // GPIO.0 .... GPIO.15 + NRF_P0->OUTCLR = + BV(4) | BV(5) | BV(8) | BV(10) | BV(11) | BV(12) | BV(13) | BV(16) | + BV(19) | BV(20) | BV(21) | BV(24) | BV(26) | BV(27); + NRF_P1->OUTCLR = + BV(3) | BV(9); + + // P0.00 / XL1: XL1 (LFXO in) + NRF_P0->PIN_CNF[0] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.01 / XL2: XL2 (LFXO out) + NRF_P0->PIN_CNF[1] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.02 / AIN0: n.c. + + // P0.03 / AIN1: LED.2P + // set drive mode = wired-or to avoid conflicts with MSP430 + // ATTENTION: we expect that MSP430 does the same (doesn't use push/pull) + NRF_P0->OUTCLR = BV(3); + NRF_P0->PIN_CNF[3] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, D0S1) | // wired-or + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.04 / AIN2: GPIO.2 + NRF_P0->PIN_CNF[4] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.05 / AIN3: GPIO.3 + NRF_P0->PIN_CNF[5] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.06: I2C.SDA + // enable internal pull-up to ensure defined state on I2C bus + NRF_P0->PIN_CNF[6] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.07: PWRGD_H + NRF_P0->PIN_CNF[7] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.08: GPIO.1 + NRF_P0->PIN_CNF[8] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.09 / NFC1: THRCTRL_H0 + + // P0.10 / NFC2: GPIO.11 + NRF_P0->PIN_CNF[10] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.11: GPIO.7 + NRF_P0->PIN_CNF[11] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.12: GPIO.10 + NRF_P0->PIN_CNF[12] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.13: GPIO.8 + NRF_P0->PIN_CNF[13] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.14: C2C.MISO + // P0.15: C2C.GPIO + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[14] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->PIN_CNF[15] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.16: GPIO.9 + NRF_P0->PIN_CNF[16] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.17: C2C.MOSI + // P0.18 / RESET: C2C.CLK + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[17] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + NRF_P0->PIN_CNF[18] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pulldown) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.19: GPIO.12 + NRF_P0->PIN_CNF[19] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.20: GPIO.13 + NRF_P0->PIN_CNF[20] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.21: GPIO.0 + NRF_P0->PIN_CNF[21] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.22: C2C.CS + // enable internal pull-up/down to ensure defined state on SPI bus + NRF_P0->PIN_CNF[22] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.23: PWRGD_L + NRF_P0->PIN_CNF[23] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.24: GPIO.14 + NRF_P0->PIN_CNF[24] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.25: MAX_INT + NRF_P0->PIN_CNF[25] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.26: GPIO.5 + NRF_P0->PIN_CNF[26] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.27: GPIO.15 + NRF_P0->PIN_CNF[27] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.28 / AIN4: n.c. + + // P0.29 / AIN5: V_SW (VCAP_SENSE) + NRF_P0->PIN_CNF[29] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.30 / AIN6: RTC_INT + NRF_P0->PIN_CNF[30] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P0.31 / AIN7: n.c. + // P1.00: n.c. + // P1.01: n.c. + // P1.02: THRCTRL.H1 + + // P1.03: GPIO.6 + NRF_P1->PIN_CNF[3] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P1.04: THRCTRL.L1 + // P1.05: n.c. + // P1.06: n.c. + // P1.07: THRCTRL.L0 + + // P1.08: I2C.SCL + // enable internal pull-up to ensure defined state on I2C bus + NRF_P1->PIN_CNF[8] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Input) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Connect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Pullup) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P1.09: GPIO.4 + NRF_P1->PIN_CNF[9] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, S0S1) | + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P1.10: n.c. + // P1.11: n.c. + // P1.12: n.c. + + // P1.13: LED.0 + // set drive mode = wired-or to avoid conflicts with MSP430 + // ATTENTION: we expect that MSP430 does the same (doesn't use push/pull) + NRF_P1->OUTCLR = BV(13); + NRF_P1->PIN_CNF[13] = + BV_BY_NAME(GPIO_PIN_CNF_DIR, Output) | + BV_BY_NAME(GPIO_PIN_CNF_INPUT, Disconnect) | + BV_BY_NAME(GPIO_PIN_CNF_PULL, Disabled) | + BV_BY_NAME(GPIO_PIN_CNF_DRIVE, D0S1) | // wired-or + BV_BY_NAME(GPIO_PIN_CNF_SENSE, Disabled); + + // P1.14: n.c. + // P1.15: n.c. + + + // init clock system + // NOTE: this should be done before initializing other peripherals + + // setup: + // HFCLK: 64MHz from XO (XO running with 32MHz) + // LFCLK: 32768Hz from XO + // TIMERx (fast clock): 16MHz from PCLK16M, 32-bit timer + // RTCx (slow clock): 32768Hz from LFCLK + + // ATTENTION: the CPU is running, so some valid configuration is already there. When we change + // the settings, we must take care that the CPU keeps running for sure after every single step. + // Hence, the order of changes has to be designed with care. + + // stop HFXO (if running), implicitly switch HFCLK to HFINT +// if (NRF_CLOCK->HFCLKSTAT & CLOCK_HFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_HFCLKSTOP = 1; + + // stop LFXO (if running) +// if (NRF_CLOCK->LFCLKSTAT & CLOCK_LFCLKSTAT_STATE_Msk) + NRF_CLOCK->TASKS_LFCLKSTOP = 1; + + // init CLOCK module + NRF_CLOCK->INTENCLR = -1u; + NRF_CLOCK->LFCLKSRC = + BV_BY_NAME(CLOCK_LFCLKSRC_SRC, Xtal) | + BV_BY_NAME(CLOCK_LFCLKSRC_BYPASS, Disabled) | + BV_BY_NAME(CLOCK_LFCLKSRC_EXTERNAL, Disabled); + NRF_CLOCK->HFXODEBOUNCE = + BV_BY_VALUE(CLOCK_HFXODEBOUNCE_HFXODEBOUNCE, 0x40); + + // start HFXO + NRF_CLOCK->EVENTS_HFCLKSTARTED = 0; + NRF_CLOCK->TASKS_HFCLKSTART = 1; + + // start LFXO + NRF_CLOCK->EVENTS_LFCLKSTARTED = 0; + NRF_CLOCK->TASKS_LFCLKSTART = 1; + + // init fast-clock timer + { + _gpi_clocks_fast_timer->TASKS_STOP = 1; + + // wait a bit to make sure that STOP gets processed + // (see spec. 4413_417 v1.0 page 422 "Task delays") + // ATTENTION: since we stopped the timer, we cannot use it to measure time. + // Therefore we use some nops here. + for (i = 4; i-- > 0;) + __NOP(); + + _gpi_clocks_fast_timer->SHORTS = 0; + _gpi_clocks_fast_timer->INTENCLR = -1u; + _gpi_clocks_fast_timer->MODE = BV_BY_NAME(TIMER_MODE_MODE, Timer); + _gpi_clocks_fast_timer->BITMODE = BV_BY_NAME(TIMER_BITMODE_BITMODE, 32Bit); + + ASSERT_CT(PCLK16_RATE == (PCLK16_RATE / GPI_FAST_CLOCK_RATE) * GPI_FAST_CLOCK_RATE, GPI_FAST_CLOCK_RATE_invalid); + ASSERT_CT(IS_POWER_OF_2(PCLK16_RATE / GPI_FAST_CLOCK_RATE), GPI_FAST_CLOCK_RATE_invalid); + ASSERT_CT(MSB(PCLK16_RATE / GPI_FAST_CLOCK_RATE) <= 9, GPI_FAST_CLOCK_RATE_invalid); + + _gpi_clocks_fast_timer->PRESCALER = + BV_BY_VALUE(TIMER_PRESCALER_PRESCALER, MSB(PCLK16_RATE / GPI_FAST_CLOCK_RATE)); + + _gpi_clocks_fast_timer->TASKS_START = 1; + } + + // init RTC (slow clock) + { + _gpi_clocks_rtc->TASKS_STOP = 1; + + // NOTE: The spec. does not mention anything that we have to wait a bit after STOP. + // If there are any troubles, check again. + + _gpi_clocks_rtc->INTENCLR = -1u; + _gpi_clocks_rtc->EVTEN = BV_BY_VALUE(RTC_EVTENSET_TICK, 1); + // EVENTS_TICK is needed for ready-polling below and (optionally) VHT + + ASSERT_CT(32768 == (32768 / GPI_SLOW_CLOCK_RATE) * GPI_SLOW_CLOCK_RATE, GPI_SLOW_CLOCK_RATE_invalid); + ASSERT_CT((32768 / GPI_SLOW_CLOCK_RATE) <= 0x1000, GPI_SLOW_CLOCK_RATE_too_low); + + _gpi_clocks_rtc->PRESCALER = (32768 / GPI_SLOW_CLOCK_RATE) - 1; + + _gpi_clocks_rtc->TASKS_START = 1; + } + + // if VHT: use PPI to connect RTC->EVENTS_TICK to TIMER->TASKS_CAPTURE + #if GPI_HYBRID_CLOCK_USE_VHT + + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].EEP = + (uintptr_t)&(_gpi_clocks_rtc->EVENTS_TICK); + + NRF_PPI->CH[GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL].TEP = + (uintptr_t)&(_gpi_clocks_fast_timer->TASKS_CAPTURE[GPI_ARM_NRF_HYBRID_CLOCK_CAPTURE_REG]); + + NRF_PPI->CHENSET = BV(GPI_ARM_NRF_HYBRID_CLOCK_PPI_CHANNEL); + + #endif + + + // init UART + // ATTENTION: before using the UART HFCLK must be stable too + // NOTE: init UART always (even if not used externally) + // because stdio functions presume it (currently) + uart_init(GPI_STDOUT_UART_BAUDRATE); + + + // wait until clocks are stable + // ATTENTION: The clocks are not reliable before. However, we can interleave all init tasks + // that do not rely on clock accuracy. + // ATTENTION: Since LFCLK may have been unstable when we initialized the RTC (see above), + // we wait until RTC started up for sure (see spec. 4413_417 v1.0 page 339 START task delay) + while (!(NRF_CLOCK->EVENTS_HFCLKSTARTED && NRF_CLOCK->EVENTS_LFCLKSTARTED)); + _gpi_clocks_rtc->EVENTS_TICK = 0; + while (!(_gpi_clocks_rtc->EVENTS_TICK)); + + // if !VHT: disable RTC->EVENTS_TICK (not needed anymore) + #if !GPI_HYBRID_CLOCK_USE_VHT + _gpi_clocks_rtc->EVTENCLR = BV_BY_VALUE(RTC_EVTENSET_TICK, 1); + #endif + + + // init TRACE DSR + #if (GPI_TRACE_MODE_IS_TRACE && GPI_TRACE_USE_DSR) + NVIC_SetPriority(GPI_TRACE_DSR_IRQ, 0xff); + NVIC_ClearPendingIRQ(GPI_TRACE_DSR_IRQ); + NVIC_EnableIRQ(GPI_TRACE_DSR_IRQ); + #endif + + + // GPI_TRACE_RETURN(); +} + +//************************************************************************************************** +//************************************************************************************************** diff --git a/src/gpi/arm/nordic/shepherd_nrf52/platform.h b/src/gpi/arm/nordic/shepherd_nrf52/platform.h new file mode 100644 index 0000000..f677fde --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/platform.h @@ -0,0 +1,257 @@ +/*************************************************************************************************** + *************************************************************************************************** + * + * Copyright (c) 2025, Networked Embedded Systems Lab, TU Dresden + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * 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. + * * Neither the name of the NES Lab or TU Dresden 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 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. + * + ***********************************************************************************************//** + * + * @file gpi/arm/nordic/shepherd_nrf52/platform.h + * + * @brief platform interface functions, specific for nRF52 Shepherd target + * + * @version $Id$ + * @date TODO + * + * @author Carsten Herrmann + * + *************************************************************************************************** + + @details + + TODO + + **************************************************************************************************/ + +#ifndef __GPI_ARM_nRF_SHEPHERD_NRF52_PLATFORM_H__ +#define __GPI_ARM_nRF_SHEPHERD_NRF52_PLATFORM_H__ + +//************************************************************************************************** +//***** Includes *********************************************************************************** + +#include "gpi/platform_spec.h" + +#include "../nrf528xx/platform.h" // nRF528xx common functionality + +#include "gpi/tools.h" + +#include + +#include + +//************************************************************************************************** +//***** Global (Public) Defines and Consts ********************************************************* + +#define GPI_LED_NONE 0 +#define GPI_LED_0 BV(24) // P1.13 (+11) +#define GPI_LED_2P BV(3) // P0.03 + +// On Shepherd targets, GPIOs are typically used as output pins (with GPIO tracing capabilities +// on the base board). Hence, we provide all such pins as LEDs for simplicity. +#define GPI_LED_GPIO0 BV(23) // P0.21 (+2) +#define GPI_LED_GPIO1 BV(8) // P0.08 +#define GPI_LED_GPIO2 BV(4) // P0.04 +#define GPI_LED_GPIO3 BV(5) // P0.05 +#define GPI_LED_GPIO4 BV(20) // P1.09 (+11) +#define GPI_LED_GPIO5 BV(28) // P0.26 (+2) +#define GPI_LED_GPIO6 BV(14) // P1.03 (+11) +#define GPI_LED_GPIO7 BV(11) // P0.11 +#define GPI_LED_GPIO8 BV(13) // P0.13 +#define GPI_LED_GPIO9 BV(18) // P0.16 (+2) +#define GPI_LED_GPIO10 BV(12) // P0.12 +#define GPI_LED_GPIO11 BV(10) // P0.10 +#define GPI_LED_GPIO12 BV(21) // P0.19 (+2) +#define GPI_LED_GPIO13 BV(22) // P0.20 (+2) +#define GPI_LED_GPIO14 BV(26) // P0.24 (+2) +#define GPI_LED_GPIO15 BV(29) // P0.27 (+2) + +#define GPI_BUTTON_PWRGD_H BV(7) // P0.07 +#define GPI_BUTTON_PWRGD_L BV(23) // P0.23 +#define GPI_BUTTON_MAX_INT BV(25) // P0.25 +#define GPI_BUTTON_RTC_INT BV(30) // P0.30 + +// internal mapping to P0 and P1 +// ATTENTION: concept works as long as bit positions at P0 and P1 do not overlap. +// If they do, shift bit mapping at P0 or P1 by a constant to find a non-overlapping combination. +// If impossible, there is more work to do. +// for details see gpi_led_on() +#define _GPI_LED_P0_MASK (GPI_LED_2P \ + | GPI_LED_GPIO0 | GPI_LED_GPIO1 | GPI_LED_GPIO2 | GPI_LED_GPIO3 \ + | GPI_LED_GPIO5 | GPI_LED_GPIO7 | GPI_LED_GPIO8 | GPI_LED_GPIO9 \ + | GPI_LED_GPIO10 | GPI_LED_GPIO11 | GPI_LED_GPIO12 | GPI_LED_GPIO13 \ + | GPI_LED_GPIO14 | GPI_LED_GPIO15 ) +#define _GPI_LED_P1_MASK (GPI_LED_0 | GPI_LED_GPIO4 | GPI_LED_GPIO6) +#define _GPI_LED_P0b_SHIFT 2 // shift between upper 16 bit of P0 and MASK +#define _GPI_LED_P1_SHIFT 11 // shift between P1 and MASK +#define _GPI_BUTTON_P0_MASK (GPI_BUTTON_PWRGD_H | GPI_BUTTON_PWRGD_L | GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT) +#define _GPI_BUTTON_P1_MASK 0 +#define _GPI_BUTTON_INV_MASK (GPI_BUTTON_MAX_INT | GPI_BUTTON_RTC_INT) + +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_led_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { + case 1: return GPI_LED_0; + case 2: return GPI_LED_2P; + case 3: return GPI_LED_GPIO0; + case 4: return GPI_LED_GPIO1; + case 5: return GPI_LED_GPIO2; + case 6: return GPI_LED_GPIO3; + case 7: return GPI_LED_GPIO4; + case 8: return GPI_LED_GPIO5; + case 9: return GPI_LED_GPIO6; + case 10: return GPI_LED_GPIO7; + case 11: return GPI_LED_GPIO8; + case 12: return GPI_LED_GPIO9; + case 13: return GPI_LED_GPIO10; + case 14: return GPI_LED_GPIO11; + case 15: return GPI_LED_GPIO12; + case 16: return GPI_LED_GPIO13; + case 17: return GPI_LED_GPIO14; + case 18: return GPI_LED_GPIO15; + default: return GPI_LED_NONE; + } +} + +// for details see comments in gpi/platform.h +static ALWAYS_INLINE int gpi_button_index_to_mask(int i) +{ + // NOTE: with optimization enabled, the switch block gets replaced by + // * a constant if i is constant (due to constant propagation) + // * a lookup table with preceding 1 <= i <= i_max test, or + // * a fast conditional execution block (on ARM) + switch (i) + { + case 1: return GPI_BUTTON_PWRGD_H; + case 2: return GPI_BUTTON_PWRGD_L; + case 3: return GPI_BUTTON_MAX_INT; + case 4: return GPI_BUTTON_RTC_INT; + default: return 0; + } +} + +//************************************************************************************************** +//***** Local (Private) Defines and Consts ********************************************************* + +// UART pins +#define _GPI_ARM_nRF_UART_TXD_PORT 0 +#define _GPI_ARM_nRF_UART_TXD_PIN 6 +#define _GPI_ARM_nRF_UART_RXD_PORT 0 +#define _GPI_ARM_nRF_UART_RXD_PIN 21 + +//************************************************************************************************** +//***** Forward Class and Struct Declarations ****************************************************** + + + +//************************************************************************************************** +//***** Global Typedefs and Class Declarations ***************************************************** + + + +//************************************************************************************************** +//***** Global Variables *************************************************************************** + + + +//************************************************************************************************** +//***** Prototypes of Global Functions ************************************************************* + +#ifdef __cplusplus + extern "C" { +#endif + + + +#ifdef __cplusplus + } +#endif + +//************************************************************************************************** +//***** Implementations of Inline Functions ******************************************************** + +static ALWAYS_INLINE void gpi_led_on(int mask) +{ + register int mask0 = mask & _GPI_LED_P0_MASK; + register int mask1 = mask & _GPI_LED_P1_MASK; + + if (mask0) + NRF_P0->OUTSET = (mask0 & 0xFFFF) | ((mask0 & 0xFFFF0000) >> _GPI_LED_P0b_SHIFT); + + if (mask1) + NRF_P1->OUTSET = mask1 >> _GPI_LED_P1_SHIFT; +} + +static ALWAYS_INLINE void gpi_led_off(int mask) +{ + register int mask0 = mask & _GPI_LED_P0_MASK; + register int mask1 = mask & _GPI_LED_P1_MASK; + + if (mask0) + NRF_P0->OUTCLR = (mask0 & 0xFFFF) | ((mask0 & 0xFFFF0000) >> _GPI_LED_P0b_SHIFT); + + if (mask1) + NRF_P1->OUTCLR = mask1 >> _GPI_LED_P1_SHIFT; +} + +static ALWAYS_INLINE void gpi_led_toggle(int mask) +{ + register int mask0 = mask & _GPI_LED_P0_MASK; + register int mask1 = mask & _GPI_LED_P1_MASK; + + if (mask0) + NRF_P0->OUT ^= (mask0 & 0xFFFF) | ((mask0 & 0xFFFF0000) >> _GPI_LED_P0b_SHIFT); + + if (mask1) + NRF_P1->OUT ^= mask1 >> _GPI_LED_P1_SHIFT; +} + +//************************************************************************************************** + +static ALWAYS_INLINE uint_fast32_t gpi_button_read(int mask) +{ + uint_fast32_t x = 0; + + if (mask & _GPI_BUTTON_P0_MASK) + x |= NRF_P0->IN & _GPI_BUTTON_P0_MASK; + + if (mask & _GPI_BUTTON_P1_MASK) + x |= NRF_P1->IN & _GPI_BUTTON_P1_MASK; + + x ^= _GPI_BUTTON_INV_MASK; + x &= mask; + + return x; +} + +//************************************************************************************************** +//************************************************************************************************** + +#endif // __GPI_ARM_nRF_SHEPHERD_NRF52_PLATFORM_H__ diff --git a/src/gpi/arm/nordic/shepherd_nrf52/profile.h b/src/gpi/arm/nordic/shepherd_nrf52/profile.h new file mode 100644 index 0000000..40394a1 --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/profile.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/profile.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/radio.h b/src/gpi/arm/nordic/shepherd_nrf52/radio.h new file mode 100644 index 0000000..f7b1a4a --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/radio.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/radio.h" diff --git a/src/gpi/arm/nordic/shepherd_nrf52/resource_check.c b/src/gpi/arm/nordic/shepherd_nrf52/resource_check.c new file mode 100644 index 0000000..9754b2f --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/resource_check.c @@ -0,0 +1,7 @@ + +#include "gpi/resource_check.h" + +// provide resource declaration symbols +#if (2 == GPI_RESOURCE_CHECK_DECLARATION) + #include "../nrf528xx/resource_declarations.h" +#endif diff --git a/src/gpi/arm/nordic/shepherd_nrf52/resource_check.h b/src/gpi/arm/nordic/shepherd_nrf52/resource_check.h new file mode 100644 index 0000000..5e15baf --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/resource_check.h @@ -0,0 +1,7 @@ + +// do not #include "gpi/resource_check.h" because order is vice versa (current file is included there) + +// provide resource declaration symbols +#if (2 != GPI_RESOURCE_CHECK_DECLARATION) + #include "../nrf528xx/resource_declarations.h" +#endif diff --git a/src/gpi/arm/nordic/shepherd_nrf52/resource_check_pre_include.h b/src/gpi/arm/nordic/shepherd_nrf52/resource_check_pre_include.h new file mode 100644 index 0000000..959b469 --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/resource_check_pre_include.h @@ -0,0 +1,3 @@ + +// nothing to do here + diff --git a/src/gpi/arm/nordic/shepherd_nrf52/trace.h b/src/gpi/arm/nordic/shepherd_nrf52/trace.h new file mode 100644 index 0000000..ebbf4cb --- /dev/null +++ b/src/gpi/arm/nordic/shepherd_nrf52/trace.h @@ -0,0 +1,4 @@ + +// functionality is not board specific -> provide simple wrapper + +#include "../nrf528xx/trace.h" diff --git a/src/gpi/clocks.h b/src/gpi/clocks.h index 66c7514..4514afe 100644 --- a/src/gpi/clocks.h +++ b/src/gpi/clocks.h @@ -1,7 +1,7 @@ /*************************************************************************************************** *************************************************************************************************** * - * Copyright (c) 2018 - 2019, Networked Embedded Systems Lab, TU Dresden + * Copyright (c) 2018 - 2024, Networked Embedded Systems Lab, TU Dresden * All rights reserved. * * Redistribution and use in source and binary forms, with or without @@ -30,18 +30,117 @@ * * @file gpi/clocks.h * - * @brief general-purpose slow (lo-res), fast (hi-res), and hybrid clock + * @brief General-purpose slow (lo-res), fast (hi-res), and hybrid clock. * - * @version $Id: a24c9a6c350043733b29a187c3a7abed7cd2b0f8 $ - * @date TODO + * @internal + * @version \$Id$ + * @noop @date git log -1 * * @author Carsten Herrmann + * @endinternal * *************************************************************************************************** @details + + This module provides basic clock functionality, which is useful in many projects. + + The common API supports three clocks, namely a *fast clock*, a *slow clock*, and + a *hybrid clock*. Depending on the platform, not each of these clocks may be independent, + but the user can expect that there is a clock behind each interface (e.g., the fast clock and + hybrid clock functions may access the same fast internal clock, while the slow clock functions + access a slower real-time clock). + Beyond the common API defined here, the platform-specific clocks.h may provide additional + functionality that is specific for the concrete platform, e.g. special synchronization routines. + It also contains platform-specific configuration settings, e.g., clock rates, + used hardware resources (e.g., timer instances), etc.. + + # Clock Types + + In a typical setting, the *fast clock* represents an everyday clock with a clock rate in the + MHz range that can be used for time measurements of all kinds. Often, it is also the source + clock for peripheral modules and hardware-based timer capture functionality. The accuracy + of the fast clock is heavily platform-dependent and can range from tens of ppm (e.g., from a + crystal oscillator) to multiple percent (e.g., from an uncompensated internal oscillator). + + The *slow clock* usually stands for a real-time clock with a clock rate of some kHz. + It is interesting in situations where longer time periods should be monitored and the high + resolution of the fast clock is not necessary. Depending on the platform, there may also be + differences between the fast and slow clock regarding power consumption and low-power modes. + A slow clock driven by a typical low-speed clock crystal often provides a pretty accurate + frequency (up to some ppm). + + The *hybrid clock* is intended to combine the best of both worlds, i.e., high resolution and + frequency stability as well as power effiency. Its implementation is heavily platform-dependent. + In the simplest case it can be just an alias for the fast clock if the latter is driven by an + accurate crystal oscillator and not stopped during sleep mode. Advanced implementations use + the hybrid clock to provide so-called *Virtual High-resolution Time (VHT)*, which extends the + capabilities of an accurate slow clock via an edge-coupled fast clock. The details of this + concept are discussed in [here](https://dl.acm.org/doi/10.1145/1791212.1791231). + + @sa GPI_FAST_CLOCK_RATE, GPI_SLOW_CLOCK_RATE, GPI_HYBRID_CLOCK_RATE - TODO + # Data Types {#gpi-clocks-datatypes} + + Clock tick values are stored in unsigned integer variables with platform-dependent width. + The API provides the following datatypes, which should be used whenever possible to avoid + hard-coded dependencies. + + Datatype | Usage + -----------------------|-------------------------------------------------- + Gpi_Fast_Tick_Native | Fast clock native datatype (machine word or smaller). + Gpi_Fast_Tick_Extended | Fast clock extended datatype (for long intervals). + Gpi_Slow_Tick_Native | Slow clock native datatype (machine word or smaller). + Gpi_Slow_Tick_Extended | Slow clock extended datatype (for long intervals). + Gpi_Hybrid_Tick | Hybrid clock datatype. + + Usually @noop (except for, e.g., 8-bit MCUs) + the *native* datatypes match the width of underlying hardware timers and / or machine words + (often 16 or 32 bit). They represent the exchange format between hardware (e.g., timer + capture units) and software and enable most efficient handling (without the need for computing + format extensions and the like). + Hence, they should be preferred whenever their width is sufficient for the application (i.e., + if wrap-arounds are detected for sure or missed wrap-arounds do not cause critical issues). + + The *extended* datatypes (often 32 or 64 bit) are provided for use cases that require to + handle longer time intervals. To this end, the underlying datatype may be larger than the + native timer and / or machine width, making it somewhat more expensive than its native + counterpart. Most important, + @attention To work correctly, format extensions beyond the width of + hardware timers require that no wrap-arounds of the underlying native timers are missed. + To this end, the application must ensure that it calls `gpi_tick_fast/slow_extended()` + at least once per native wrap-around interval. + + In many applications this comes for free because the function is called anyhow once in a while + (that's what the extended datatype is needed for). Beyond that, it could be called periodically + in a main loop or an interrupt service routine. + + The hybrid clock does not provide an explicit native datatype, as the handling of + `Gpi_Hybrid_Tick` is always special if the hybrid clock is not just an alias for another clock. + + @todo Rename datatypes to `Gpi_Tick_...` and mark current names as deprecated. + + @sa + GPI_TICK_FAST_MAX, GPI_TICK_SLOW_MAX, GPI_TICK_HYBRID_MAX + \ref gpi-clocks-compare + + # Comparing Timestamps {#gpi-clocks-compare} + + Since tick values have limited width and overflow after a specific period of time, the + comparison of tick values must be handled in a way that is aware of potential wrap-arounds. + For example, to compare if `uint16_t t1` represents an earlier point in time than `uint16_t t2` + it is normally not a good idea to evaluate `t1 < t2` (e.g., think of the case `t1 = 0xfffe` + and `t2 = 0x0001`). In many cases, it is better to evaluate `t2 - t1` and test if the result + is positive or negative, as in `(int16_t)(t2 - t1) > 0`. Unfortunately the cast to the signed + datatype requires knowledge about the timestamp datatype (`uint16_t` in the example), which + is not directly available when using GPI tick datatypes. Much worse, the program would have to + handle multiple alternative type definitions if the source code shall be platform-independent. + + To address this problem, the GPI provides explicit helper functions for timestamp comparisons. + They are named `gpi_tick_compare_(a, b)` and return + -1 if \a a is earlier than \a b (i.e., \a a - \a b < 0), + +1 if \a a is later than \a b (i.e., \a a - \a b > 0), + and 0 if \a a equals \a b. **************************************************************************************************/ @@ -57,33 +156,126 @@ //************************************************************************************************** //***** Global (Public) Defines and Consts ********************************************************* +/// @name Clock Rates +// The following definitions are platform-specific, but mandatory. +/// @{ + +/// @def GPI_FAST_CLOCK_RATE +/// @brief Clock frequency of fast clock in ticks per second. + +/// @def GPI_SLOW_CLOCK_RATE +/// @brief Clock frequency of slow clock in ticks per second. + +/// @def GPI_HYBRID_CLOCK_RATE +/// @brief Clock frequency of hybrid clock in ticks per second. + +#ifdef __DOXYGEN__ + // provide dummy definitions if necessary to avoid incomplete documentation + #ifndef GPI_FAST_CLOCK_RATE + #define GPI_FAST_CLOCK_RATE + #endif + #ifndef GPI_SLOW_CLOCK_RATE + #define GPI_SLOW_CLOCK_RATE + #endif + #ifndef GPI_HYBRID_CLOCK_RATE + #define GPI_HYBRID_CLOCK_RATE + #endif +#endif + +/// @} + +/// @name Clock Value Ranges +/// The following macros determine the value ranges of the different clock types (fast, slow, hyrid). +/// They mark the highest possible value, i.e., the value one tick before the clock value wraps +/// around to zero. +/// Note that these values can be smaller than the corresponding datatype's maxima (for example, +/// the tick datatype could be uint32_t while the clock is a 24 bit timer with +/// `GPI_TICK_..._MAX = 0xffffff`). +/// @todo Add equivalent macros for native datatypes +/// @{ + +/// @brief Maximum fast clock (extended) tick value. #ifndef GPI_TICK_FAST_MAX #define GPI_TICK_FAST_MAX ((Gpi_Fast_Tick_Extended)(UINT64_C(0xFFFFFFFFFFFFFFFF))) #endif +/// @brief Maximum slow clock (extended) tick value. #ifndef GPI_TICK_SLOW_MAX #define GPI_TICK_SLOW_MAX ((Gpi_Slow_Tick_Extended)(UINT64_C(0xFFFFFFFFFFFFFFFF))) #endif +/// @brief Maximum hybrid clock tick value. #ifndef GPI_TICK_HYBRID_MAX #define GPI_TICK_HYBRID_MAX ((Gpi_Hybrid_Tick)(UINT64_C(0xFFFFFFFFFFFFFFFF))) #endif +/// @} + +/// @name Time Period to Clock Ticks Conversion Macros +/// The following macros can be used to convert time periods represented in seconds, milliseconds, +/// or microseconds to the corresponding tick values (i.e., translate to the selected clock's rate). +/// The macros implement careful rounding and overflow protection. +/// They are intended to be used with constant arguments. +/// @{ + +/// Convert microseconds to fast clock ticks. #define GPI_TICK_US_TO_FAST(us) _GPI_TICK_T_TO_X(us, 1000000, FAST) + +/// Convert milliseconds to fast clock ticks. #define GPI_TICK_MS_TO_FAST(ms) _GPI_TICK_T_TO_X(ms, 1000, FAST) + +/// Convert seconds to fast clock ticks. #define GPI_TICK_S_TO_FAST(s) _GPI_TICK_T_TO_X(s, 1, FAST) + +/// @brief Convert milliseconds to fast clock ticks with second split. +/// @details +/// This macro can be used if \a ms is too large for GPI_TICK_MS_TO_FAST(). +/// With GPI_TICK_MS_TO_FAST2(), the value is split into a seconds (\a ms / 1000) and a milliseconds +/// (\a ms % 1000) part and then converted separately. This allows for larger value ranges, but +/// the result may be less accurate than that from GPI_TICK_MS_TO_FAST(). #define GPI_TICK_MS_TO_FAST2(ms) ( GPI_TICK_S_TO_FAST( (ms) / 1000) + GPI_TICK_MS_TO_FAST((ms) % 1000) ) + +/// @brief Convert microseconds to fast clock ticks with millisecond split. +/// @details +/// This macro can be used if \a us is too large for GPI_TICK_US_TO_FAST(). +/// With GPI_TICK_US_TO_FAST2(), the value is split into a milliseconds (\a us / 1000) and a +/// microseconds (\a us % 1000) part and then converted separately. This allows for larger value +/// ranges, but the result may be less accurate than that from GPI_TICK_US_TO_FAST(). #define GPI_TICK_US_TO_FAST2(us) ( GPI_TICK_MS_TO_FAST2((us) / 1000) + GPI_TICK_US_TO_FAST((us) % 1000) ) +/// Convert milliseconds to slow clock ticks. #define GPI_TICK_MS_TO_SLOW(ms) _GPI_TICK_T_TO_X(ms, 1000, SLOW) + +/// Convert seconds to slow clock ticks. #define GPI_TICK_S_TO_SLOW(s) _GPI_TICK_T_TO_X(s, 1, SLOW) +/// Convert microseconds to hybrid clock ticks. #define GPI_TICK_US_TO_HYBRID(us) _GPI_TICK_T_TO_X(us, 1000000, HYBRID) + +/// Convert milliseconds to hybrid clock ticks. #define GPI_TICK_MS_TO_HYBRID(ms) _GPI_TICK_T_TO_X(ms, 1000, HYBRID) + +/// Convert seconds to hybrid clock ticks. #define GPI_TICK_S_TO_HYBRID(s) _GPI_TICK_T_TO_X(s, 1, HYBRID) + +/// @brief Convert milliseconds to hybrid clock ticks with second split. +/// @details +/// This macro can be used if \a ms is too large for GPI_TICK_MS_TO_HYBRID(). +/// With GPI_TICK_MS_TO_HYBRID2(), the value is split into a seconds (\a ms / 1000) and a milliseconds +/// (\a ms % 1000) part and then converted separately. This allows for larger value ranges, but +/// the result may be less accurate than that from GPI_TICK_MS_TO_HYBRID(). #define GPI_TICK_MS_TO_HYBRID2(ms) ( GPI_TICK_S_TO_HYBRID( (ms) / 1000) + GPI_TICK_MS_TO_HYBRID((ms) % 1000) ) + +/// @brief Convert microseconds to hybrid clock ticks with millisecond split. +/// @details +/// This macro can be used if \a us is too large for GPI_TICK_US_TO_HYBRID(). +/// With GPI_TICK_US_TO_HYBRID2(), the value is split into a milliseconds (\a us / 1000) and a +/// microseconds (\a us % 1000) part and then converted separately. This allows for larger value +/// ranges, but the result may be less accurate than that from GPI_TICK_US_TO_HYBRID(). #define GPI_TICK_US_TO_HYBRID2(us) ( GPI_TICK_MS_TO_HYBRID2((us) / 1000) + GPI_TICK_US_TO_HYBRID((us) % 1000) ) +/// @} + //************************************************************************************************** //***** Local (Private) Defines and Consts ********************************************************* @@ -119,7 +311,30 @@ //************************************************************************************************** //***** Global Typedefs and Class Declarations ***************************************************** +/// @name Tick Datatypes. +/// See \ref gpi-clocks-datatypes for details. +/// @{ + +/// @typedef Gpi_Slow_Tick_Native +/// @brief Slow clock native datatype. + +/// @typedef Gpi_Slow_Tick_Extended +/// @brief Slow clock extended datatype. + +/// @typedef Gpi_Fast_Tick_Native +/// @brief Fast clock native datatype. +/// @typedef Gpi_Fast_Tick_Extended +/// @brief Fast clock extended datatype. + +/// @typedef Gpi_Hybrid_Tick +/// @brief Hybrid clock datatype. + +/// @struct Gpi_Hybrid_Reference +/// @brief Reference value that marks relationship between fast and hybrid clock. +/// @sa gpi_tick_hybrid_reference() + +/// @} //************************************************************************************************** //***** Global Variables *************************************************************************** @@ -133,38 +348,122 @@ extern "C" { #endif -Gpi_Slow_Tick_Native gpi_tick_slow_native(); -Gpi_Slow_Tick_Extended gpi_tick_slow_extended(); +/// @name Get Time Functions +/// @{ + +/// @brief Get current time (tick value) of slow clock with native datatype. +Gpi_Slow_Tick_Native gpi_tick_slow_native(void); + +/// @brief Get current time (tick value) of slow clock with extended datatype. +Gpi_Slow_Tick_Extended gpi_tick_slow_extended(void); // provide these functions if they would be helpful, decide later //uint16_t gpi_tick_slow_16(); //uint32_t gpi_tick_slow_32(); -Gpi_Fast_Tick_Native gpi_tick_fast_native(); -Gpi_Fast_Tick_Extended gpi_tick_fast_extended(); +/// @brief Get current time (tick value) of fast clock with native datatype. +Gpi_Fast_Tick_Native gpi_tick_fast_native(void); + +/// @brief Get current time (tick value) of fast clock with extended datatype. +Gpi_Fast_Tick_Extended gpi_tick_fast_extended(void); // provide these functions if they would be helpful (and if they are available), decide later //uint16_t gpi_tick_fast_16(); //uint32_t gpi_tick_fast_32(); //uint64_t gpi_tick_fast_64(); -Gpi_Hybrid_Tick gpi_tick_hybrid(); -Gpi_Hybrid_Reference gpi_tick_hybrid_reference(); +/// @brief Get current time (tick value) of hybrid clock. +Gpi_Hybrid_Tick gpi_tick_hybrid(void); + +/// @} + +/// @name Timestamp Conversion Functions +/// @{ + +/// @brief Get reference value needed to compute relationship between fast and hybrid clock. +/// @details +/// The result contains a fast and a hybrid timestamp, which both belong to the same point in time. +/// They stem from the near past and can be used to compute the relationship for other timestamps +/// by evaluating the differences between the timestamps of interest and the reference timestamps. +Gpi_Hybrid_Reference gpi_tick_hybrid_reference(void); + +/// @brief Convert a fast tick value from the near past to hybrid ticks. +/// @details +/// This function can be used to convert a native fast clock timestamp to the corresponding +/// hybrid time. A typical use case is the conversion of a timestamp that has been captured +/// automatically in response to some hardware event (e.g., using timer capture functionality). +/// +/// To work correctly, the function must be called not more than GPI_TICK_FAST_MAX ticks +/// after the fast timestamp has been captured. Otherwise, the fast tick datatype overflow +/// leads to a wrong interpretation and result. +/// +/// @sa gpi_tick_hybrid_reference() Gpi_Hybrid_Tick gpi_tick_fast_to_hybrid(Gpi_Fast_Tick_Native fast_tick); +/// @brief Convert fast clock timestamp to microseconds. +/// @details +/// While the mathematical operation is rather simple, the GPI provides this explicit function +/// to ensure an efficient implementation. The latter is particularly important for log / trace +/// messages (which typically contain timestamps) and profiling purposes. uint32_t gpi_tick_fast_to_us(Gpi_Fast_Tick_Extended ticks); + +/// @brief Convert slow clock timestamp to microseconds. +/// @copydetails gpi_tick_fast_to_us() uint32_t gpi_tick_slow_to_us(Gpi_Slow_Tick_Extended ticks); + +/// @brief Convert hybrid clock timestamp to microseconds. +/// @copydetails gpi_tick_fast_to_us() uint32_t gpi_tick_hybrid_to_us(Gpi_Hybrid_Tick ticks); +/// @} + +/// @name Miscellaneous Functions + +/// @brief Sleep \a ms milliseconds. +/// @details +/// The function is designed carefully to guarantee that the effective delay is *at least* +/// as long as specified, which is important if, for instance, the function is used to wait +/// for some critical time constant specified in a datasheet. +/// +/// @sa gpi_micro_sleep() void gpi_milli_sleep(uint16_t ms); + +/// @brief Sleep \a us microseconds. +/// @details +/// The function is designed carefully to guarantee that the effective delay is *at least* +/// as long as specified, which is important if, for instance, the function is used to wait +/// for some critical time constant specified in a datasheet. +/// +/// Compared to gpi_milli_sleep(), gpi_micro_sleep() is explicitly tuned to be fast +/// beyond the specified sleep period and to make the effective delay as accurate as possible. +/// +/// @sa gpi_milli_sleep() void gpi_micro_sleep(uint16_t us); +/// @} + +/// @name Timestamp Comparison Functions +/// See \ref gpi-clocks-compare for details. +/// @{ + +/// @brief Test if \a a is earlier, equal, or later than \a b. +/// @details For details see \ref gpi-clocks-compare. static inline int_fast8_t gpi_tick_compare_slow_native(Gpi_Slow_Tick_Native a, Gpi_Slow_Tick_Native b); + +/// @copydoc gpi_tick_compare_slow_native() static inline int_fast8_t gpi_tick_compare_slow_extended(Gpi_Slow_Tick_Extended a, Gpi_Slow_Tick_Extended b); + +/// @copydoc gpi_tick_compare_slow_native() static inline int_fast8_t gpi_tick_compare_fast_native(Gpi_Fast_Tick_Native a, Gpi_Fast_Tick_Native b); + +/// @copydoc gpi_tick_compare_slow_native() static inline int_fast8_t gpi_tick_compare_fast_extended(Gpi_Fast_Tick_Extended a, Gpi_Fast_Tick_Extended b); + +/// @copydoc gpi_tick_compare_slow_native() static inline int_fast8_t gpi_tick_compare_hybrid(Gpi_Hybrid_Tick a, Gpi_Hybrid_Tick b); +/// @} + #ifdef __cplusplus } #endif diff --git a/src/gpi/doc/doxyfile b/src/gpi/doc/doxyfile new file mode 100644 index 0000000..4f650ab --- /dev/null +++ b/src/gpi/doc/doxyfile @@ -0,0 +1,2894 @@ +# Doxyfile 1.10.0 + +# This file describes the settings to be used by the documentation system +# doxygen (www.doxygen.org) for a project. +# +# All text after a double hash (##) is considered a comment and is placed in +# front of the TAG it is preceding. +# +# All text after a single hash (#) is considered a comment and will be ignored. +# The format is: +# TAG = value [value, ...] +# For lists, items can also be appended using: +# TAG += value [value, ...] +# Values that contain spaces should be placed between quotes (\" \"). +# +# Note: +# +# Use doxygen to compare the used configuration file with the template +# configuration file: +# doxygen -x [configFile] +# Use doxygen to compare the used configuration file with the template +# configuration file without replacing the environment variables or CMake type +# replacement variables: +# doxygen -x_noenv [configFile] + +#--------------------------------------------------------------------------- +# Project related configuration options +#--------------------------------------------------------------------------- + +# This tag specifies the encoding used for all characters in the configuration +# file that follow. The default is UTF-8 which is also the encoding used for all +# text before the first occurrence of this tag. Doxygen uses libiconv (or the +# iconv built into libc) for the transcoding. See +# https://www.gnu.org/software/libiconv/ for the list of possible encodings. +# The default value is: UTF-8. + +DOXYFILE_ENCODING = UTF-8 + +# The PROJECT_NAME tag is a single word (or a sequence of words surrounded by +# double-quotes, unless you are using Doxywizard) that should identify the +# project for which the documentation is generated. This name is used in the +# title of most generated pages and in a few other places. +# The default value is: My Project. + +PROJECT_NAME = GPI + +# The PROJECT_NUMBER tag can be used to enter a project or revision number. This +# could be handy for archiving the generated documentation or if some version +# control system is used. + +PROJECT_NUMBER = "" + +# Using the PROJECT_BRIEF tag one can provide an optional one line description +# for a project that appears at the top of each page and should give viewer a +# quick idea about the purpose of the project. Keep the description short. + +PROJECT_BRIEF = "" + +# With the PROJECT_LOGO tag one can specify a logo or an icon that is included +# in the documentation. The maximum height of the logo should not exceed 55 +# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy +# the logo to the output directory. + +PROJECT_LOGO = + +# With the PROJECT_ICON tag one can specify an icon that is included in the tabs +# when the HTML document is shown. Doxygen will copy the logo to the output +# directory. + +PROJECT_ICON = + +# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path +# into which the generated documentation will be written. If a relative path is +# entered, it will be relative to the location where doxygen was started. If +# left blank the current directory will be used. + +OUTPUT_DIRECTORY = . + +# If the CREATE_SUBDIRS tag is set to YES then doxygen will create up to 4096 +# sub-directories (in 2 levels) under the output directory of each output format +# and will distribute the generated files over these directories. Enabling this +# option can be useful when feeding doxygen a huge amount of source files, where +# putting all generated files in the same directory would otherwise causes +# performance problems for the file system. Adapt CREATE_SUBDIRS_LEVEL to +# control the number of sub-directories. +# The default value is: NO. + +CREATE_SUBDIRS = NO + +# Controls the number of sub-directories that will be created when +# CREATE_SUBDIRS tag is set to YES. Level 0 represents 16 directories, and every +# level increment doubles the number of directories, resulting in 4096 +# directories at level 8 which is the default and also the maximum value. The +# sub-directories are organized in 2 levels, the first level always has a fixed +# number of 16 directories. +# Minimum value: 0, maximum value: 8, default value: 8. +# This tag requires that the tag CREATE_SUBDIRS is set to YES. + +CREATE_SUBDIRS_LEVEL = 8 + +# If the ALLOW_UNICODE_NAMES tag is set to YES, doxygen will allow non-ASCII +# characters to appear in the names of generated files. If set to NO, non-ASCII +# characters will be escaped, for example _xE3_x81_x84 will be used for Unicode +# U+3044. +# The default value is: NO. + +ALLOW_UNICODE_NAMES = NO + +# The OUTPUT_LANGUAGE tag is used to specify the language in which all +# documentation generated by doxygen is written. Doxygen will use this +# information to generate all constant output in the proper language. +# Possible values are: Afrikaans, Arabic, Armenian, Brazilian, Bulgarian, +# Catalan, Chinese, Chinese-Traditional, Croatian, Czech, Danish, Dutch, English +# (United States), Esperanto, Farsi (Persian), Finnish, French, German, Greek, +# Hindi, Hungarian, Indonesian, Italian, Japanese, Japanese-en (Japanese with +# English messages), Korean, Korean-en (Korean with English messages), Latvian, +# Lithuanian, Macedonian, Norwegian, Persian (Farsi), Polish, Portuguese, +# Romanian, Russian, Serbian, Serbian-Cyrillic, Slovak, Slovene, Spanish, +# Swedish, Turkish, Ukrainian and Vietnamese. +# The default value is: English. + +OUTPUT_LANGUAGE = English + +# If the BRIEF_MEMBER_DESC tag is set to YES, doxygen will include brief member +# descriptions after the members that are listed in the file and class +# documentation (similar to Javadoc). Set to NO to disable this. +# The default value is: YES. + +BRIEF_MEMBER_DESC = YES + +# If the REPEAT_BRIEF tag is set to YES, doxygen will prepend the brief +# description of a member or function before the detailed description +# +# Note: If both HIDE_UNDOC_MEMBERS and BRIEF_MEMBER_DESC are set to NO, the +# brief descriptions will be completely suppressed. +# The default value is: YES. + +REPEAT_BRIEF = YES + +# This tag implements a quasi-intelligent brief description abbreviator that is +# used to form the text in various listings. Each string in this list, if found +# as the leading text of the brief description, will be stripped from the text +# and the result, after processing the whole list, is used as the annotated +# text. Otherwise, the brief description is used as-is. If left blank, the +# following values are used ($name is automatically replaced with the name of +# the entity):The $name class, The $name widget, The $name file, is, provides, +# specifies, contains, represents, a, an and the. + +ABBREVIATE_BRIEF = "The $name class" \ + "The $name widget" \ + "The $name file" \ + is \ + provides \ + specifies \ + contains \ + represents \ + a \ + an \ + the + +# If the ALWAYS_DETAILED_SEC and REPEAT_BRIEF tags are both set to YES then +# doxygen will generate a detailed section even if there is only a brief +# description. +# The default value is: NO. + +ALWAYS_DETAILED_SEC = NO + +# If the INLINE_INHERITED_MEMB tag is set to YES, doxygen will show all +# inherited members of a class in the documentation of that class as if those +# members were ordinary class members. Constructors, destructors and assignment +# operators of the base classes will not be shown. +# The default value is: NO. + +INLINE_INHERITED_MEMB = NO + +# If the FULL_PATH_NAMES tag is set to YES, doxygen will prepend the full path +# before files name in the file list and in the header files. If set to NO the +# shortest path that makes the file name unique will be used +# The default value is: YES. + +FULL_PATH_NAMES = NO + +# The STRIP_FROM_PATH tag can be used to strip a user-defined part of the path. +# Stripping is only done if one of the specified strings matches the left-hand +# part of the path. The tag can be used to show relative paths in the file list. +# If left blank the directory from which doxygen is run is used as the path to +# strip. +# +# Note that you can specify absolute paths here, but also relative paths, which +# will be relative from the directory where doxygen is started. +# This tag requires that the tag FULL_PATH_NAMES is set to YES. + +STRIP_FROM_PATH = + +# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the +# path mentioned in the documentation of a class, which tells the reader which +# header file to include in order to use a class. If left blank only the name of +# the header file containing the class definition is used. Otherwise one should +# specify the list of include paths that are normally passed to the compiler +# using the -I flag. + +STRIP_FROM_INC_PATH = + +# If the SHORT_NAMES tag is set to YES, doxygen will generate much shorter (but +# less readable) file names. This can be useful is your file systems doesn't +# support long names like on DOS, Mac, or CD-ROM. +# The default value is: NO. + +SHORT_NAMES = NO + +# If the JAVADOC_AUTOBRIEF tag is set to YES then doxygen will interpret the +# first line (until the first dot) of a Javadoc-style comment as the brief +# description. If set to NO, the Javadoc-style will behave just like regular Qt- +# style comments (thus requiring an explicit @brief command for a brief +# description.) +# The default value is: NO. + +JAVADOC_AUTOBRIEF = NO + +# If the JAVADOC_BANNER tag is set to YES then doxygen will interpret a line +# such as +# /*************** +# as being the beginning of a Javadoc-style comment "banner". If set to NO, the +# Javadoc-style will behave just like regular comments and it will not be +# interpreted by doxygen. +# The default value is: NO. + +JAVADOC_BANNER = NO + +# If the QT_AUTOBRIEF tag is set to YES then doxygen will interpret the first +# line (until the first dot) of a Qt-style comment as the brief description. If +# set to NO, the Qt-style will behave just like regular Qt-style comments (thus +# requiring an explicit \brief command for a brief description.) +# The default value is: NO. + +QT_AUTOBRIEF = NO + +# The MULTILINE_CPP_IS_BRIEF tag can be set to YES to make doxygen treat a +# multi-line C++ special comment block (i.e. a block of //! or /// comments) as +# a brief description. This used to be the default behavior. The new default is +# to treat a multi-line C++ comment block as a detailed description. Set this +# tag to YES if you prefer the old behavior instead. +# +# Note that setting this tag to YES also means that rational rose comments are +# not recognized any more. +# The default value is: NO. + +MULTILINE_CPP_IS_BRIEF = NO + +# By default Python docstrings are displayed as preformatted text and doxygen's +# special commands cannot be used. By setting PYTHON_DOCSTRING to NO the +# doxygen's special commands can be used and the contents of the docstring +# documentation blocks is shown as doxygen documentation. +# The default value is: YES. + +PYTHON_DOCSTRING = YES + +# If the INHERIT_DOCS tag is set to YES then an undocumented member inherits the +# documentation from any documented member that it re-implements. +# The default value is: YES. + +INHERIT_DOCS = YES + +# If the SEPARATE_MEMBER_PAGES tag is set to YES then doxygen will produce a new +# page for each member. If set to NO, the documentation of a member will be part +# of the file/class/namespace that contains it. +# The default value is: NO. + +SEPARATE_MEMBER_PAGES = NO + +# The TAB_SIZE tag can be used to set the number of spaces in a tab. Doxygen +# uses this value to replace tabs by spaces in code fragments. +# Minimum value: 1, maximum value: 16, default value: 4. + +TAB_SIZE = 4 + +# This tag can be used to specify a number of aliases that act as commands in +# the documentation. An alias has the form: +# name=value +# For example adding +# "sideeffect=@par Side Effects:^^" +# will allow you to put the command \sideeffect (or @sideeffect) in the +# documentation, which will result in a user-defined paragraph with heading +# "Side Effects:". Note that you cannot put \n's in the value part of an alias +# to insert newlines (in the resulting output). You can put ^^ in the value part +# of an alias to insert a newline as if a physical newline was in the original +# file. When you need a literal { or } or , in the value part of an alias you +# have to escape them by means of a backslash (\), this can lead to conflicts +# with the commands \{ and \} for these it is advised to use the version @{ and +# @} or use a double escape (\\{ and \\}) + +ALIASES = + +# Set the OPTIMIZE_OUTPUT_FOR_C tag to YES if your project consists of C sources +# only. Doxygen will then generate output that is more tailored for C. For +# instance, some of the names that are used will be different. The list of all +# members will be omitted, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_FOR_C = YES + +# Set the OPTIMIZE_OUTPUT_JAVA tag to YES if your project consists of Java or +# Python sources only. Doxygen will then generate output that is more tailored +# for that language. For instance, namespaces will be presented as packages, +# qualified scopes will look different, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_JAVA = NO + +# Set the OPTIMIZE_FOR_FORTRAN tag to YES if your project consists of Fortran +# sources. Doxygen will then generate output that is tailored for Fortran. +# The default value is: NO. + +OPTIMIZE_FOR_FORTRAN = NO + +# Set the OPTIMIZE_OUTPUT_VHDL tag to YES if your project consists of VHDL +# sources. Doxygen will then generate output that is tailored for VHDL. +# The default value is: NO. + +OPTIMIZE_OUTPUT_VHDL = NO + +# Set the OPTIMIZE_OUTPUT_SLICE tag to YES if your project consists of Slice +# sources only. Doxygen will then generate output that is more tailored for that +# language. For instance, namespaces will be presented as modules, types will be +# separated into more groups, etc. +# The default value is: NO. + +OPTIMIZE_OUTPUT_SLICE = NO + +# Doxygen selects the parser to use depending on the extension of the files it +# parses. With this tag you can assign which parser to use for a given +# extension. Doxygen has a built-in mapping, but you can override or extend it +# using this tag. The format is ext=language, where ext is a file extension, and +# language is one of the parsers supported by doxygen: IDL, Java, JavaScript, +# Csharp (C#), C, C++, Lex, D, PHP, md (Markdown), Objective-C, Python, Slice, +# VHDL, Fortran (fixed format Fortran: FortranFixed, free formatted Fortran: +# FortranFree, unknown formatted Fortran: Fortran. In the later case the parser +# tries to guess whether the code is fixed or free formatted code, this is the +# default for Fortran type files). For instance to make doxygen treat .inc files +# as Fortran files (default is PHP), and .f files as C (default is Fortran), +# use: inc=Fortran f=C. +# +# Note: For files without extension you can use no_extension as a placeholder. +# +# Note that for custom extensions you also need to set FILE_PATTERNS otherwise +# the files are not read by doxygen. When specifying no_extension you should add +# * to the FILE_PATTERNS. +# +# Note see also the list of default file extension mappings. + +EXTENSION_MAPPING = + +# If the MARKDOWN_SUPPORT tag is enabled then doxygen pre-processes all comments +# according to the Markdown format, which allows for more readable +# documentation. See https://daringfireball.net/projects/markdown/ for details. +# The output of markdown processing is further processed by doxygen, so you can +# mix doxygen, HTML, and XML commands with Markdown formatting. Disable only in +# case of backward compatibilities issues. +# The default value is: YES. + +MARKDOWN_SUPPORT = YES + +# When the TOC_INCLUDE_HEADINGS tag is set to a non-zero value, all headings up +# to that level are automatically included in the table of contents, even if +# they do not have an id attribute. +# Note: This feature currently applies only to Markdown headings. +# Minimum value: 0, maximum value: 99, default value: 5. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +TOC_INCLUDE_HEADINGS = 5 + +# The MARKDOWN_ID_STYLE tag can be used to specify the algorithm used to +# generate identifiers for the Markdown headings. Note: Every identifier is +# unique. +# Possible values are: DOXYGEN use a fixed 'autotoc_md' string followed by a +# sequence number starting at 0 and GITHUB use the lower case version of title +# with any whitespace replaced by '-' and punctuation characters removed. +# The default value is: DOXYGEN. +# This tag requires that the tag MARKDOWN_SUPPORT is set to YES. + +MARKDOWN_ID_STYLE = DOXYGEN + +# When enabled doxygen tries to link words that correspond to documented +# classes, or namespaces to their corresponding documentation. Such a link can +# be prevented in individual cases by putting a % sign in front of the word or +# globally by setting AUTOLINK_SUPPORT to NO. +# The default value is: YES. + +AUTOLINK_SUPPORT = YES + +# If you use STL classes (i.e. std::string, std::vector, etc.) but do not want +# to include (a tag file for) the STL sources as input, then you should set this +# tag to YES in order to let doxygen match functions declarations and +# definitions whose arguments contain STL classes (e.g. func(std::string); +# versus func(std::string) {}). This also make the inheritance and collaboration +# diagrams that involve STL classes more complete and accurate. +# The default value is: NO. + +BUILTIN_STL_SUPPORT = NO + +# If you use Microsoft's C++/CLI language, you should set this option to YES to +# enable parsing support. +# The default value is: NO. + +CPP_CLI_SUPPORT = NO + +# Set the SIP_SUPPORT tag to YES if your project consists of sip (see: +# https://www.riverbankcomputing.com/software/sip/intro) sources only. Doxygen +# will parse them like normal C++ but will assume all classes use public instead +# of private inheritance when no explicit protection keyword is present. +# The default value is: NO. + +SIP_SUPPORT = NO + +# For Microsoft's IDL there are propget and propput attributes to indicate +# getter and setter methods for a property. Setting this option to YES will make +# doxygen to replace the get and set methods by a property in the documentation. +# This will only work if the methods are indeed getting or setting a simple +# type. If this is not the case, or you want to show the methods anyway, you +# should set this option to NO. +# The default value is: YES. + +IDL_PROPERTY_SUPPORT = YES + +# If member grouping is used in the documentation and the DISTRIBUTE_GROUP_DOC +# tag is set to YES then doxygen will reuse the documentation of the first +# member in the group (if any) for the other members of the group. By default +# all members of a group must be documented explicitly. +# The default value is: NO. + +DISTRIBUTE_GROUP_DOC = NO + +# If one adds a struct or class to a group and this option is enabled, then also +# any nested class or struct is added to the same group. By default this option +# is disabled and one has to add nested compounds explicitly via \ingroup. +# The default value is: NO. + +GROUP_NESTED_COMPOUNDS = NO + +# Set the SUBGROUPING tag to YES to allow class member groups of the same type +# (for instance a group of public functions) to be put as a subgroup of that +# type (e.g. under the Public Functions section). Set it to NO to prevent +# subgrouping. Alternatively, this can be done per class using the +# \nosubgrouping command. +# The default value is: YES. + +SUBGROUPING = YES + +# When the INLINE_GROUPED_CLASSES tag is set to YES, classes, structs and unions +# are shown inside the group in which they are included (e.g. using \ingroup) +# instead of on a separate page (for HTML and Man pages) or section (for LaTeX +# and RTF). +# +# Note that this feature does not work in combination with +# SEPARATE_MEMBER_PAGES. +# The default value is: NO. + +INLINE_GROUPED_CLASSES = NO + +# When the INLINE_SIMPLE_STRUCTS tag is set to YES, structs, classes, and unions +# with only public data fields or simple typedef fields will be shown inline in +# the documentation of the scope in which they are defined (i.e. file, +# namespace, or group documentation), provided this scope is documented. If set +# to NO, structs, classes, and unions are shown on a separate page (for HTML and +# Man pages) or section (for LaTeX and RTF). +# The default value is: NO. + +INLINE_SIMPLE_STRUCTS = NO + +# When TYPEDEF_HIDES_STRUCT tag is enabled, a typedef of a struct, union, or +# enum is documented as struct, union, or enum with the name of the typedef. So +# typedef struct TypeS {} TypeT, will appear in the documentation as a struct +# with name TypeT. When disabled the typedef will appear as a member of a file, +# namespace, or class. And the struct will be named TypeS. This can typically be +# useful for C code in case the coding convention dictates that all compound +# types are typedef'ed and only the typedef is referenced, never the tag name. +# The default value is: NO. + +TYPEDEF_HIDES_STRUCT = YES + +# The size of the symbol lookup cache can be set using LOOKUP_CACHE_SIZE. This +# cache is used to resolve symbols given their name and scope. Since this can be +# an expensive process and often the same symbol appears multiple times in the +# code, doxygen keeps a cache of pre-resolved symbols. If the cache is too small +# doxygen will become slower. If the cache is too large, memory is wasted. The +# cache size is given by this formula: 2^(16+LOOKUP_CACHE_SIZE). The valid range +# is 0..9, the default is 0, corresponding to a cache size of 2^16=65536 +# symbols. At the end of a run doxygen will report the cache usage and suggest +# the optimal cache size from a speed point of view. +# Minimum value: 0, maximum value: 9, default value: 0. + +LOOKUP_CACHE_SIZE = 0 + +# The NUM_PROC_THREADS specifies the number of threads doxygen is allowed to use +# during processing. When set to 0 doxygen will based this on the number of +# cores available in the system. You can set it explicitly to a value larger +# than 0 to get more control over the balance between CPU load and processing +# speed. At this moment only the input processing can be done using multiple +# threads. Since this is still an experimental feature the default is set to 1, +# which effectively disables parallel processing. Please report any issues you +# encounter. Generating dot graphs in parallel is controlled by the +# DOT_NUM_THREADS setting. +# Minimum value: 0, maximum value: 32, default value: 1. + +NUM_PROC_THREADS = 1 + +# If the TIMESTAMP tag is set different from NO then each generated page will +# contain the date or date and time when the page was generated. Setting this to +# NO can help when comparing the output of multiple runs. +# Possible values are: YES, NO, DATETIME and DATE. +# The default value is: NO. + +TIMESTAMP = DATE + +#--------------------------------------------------------------------------- +# Build related configuration options +#--------------------------------------------------------------------------- + +# If the EXTRACT_ALL tag is set to YES, doxygen will assume all entities in +# documentation are documented, even if no documentation was available. Private +# class members and static file members will be hidden unless the +# EXTRACT_PRIVATE respectively EXTRACT_STATIC tags are set to YES. +# Note: This will also disable the warnings about undocumented members that are +# normally produced when WARNINGS is set to YES. +# The default value is: NO. + +EXTRACT_ALL = NO + +# If the EXTRACT_PRIVATE tag is set to YES, all private members of a class will +# be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIVATE = NO + +# If the EXTRACT_PRIV_VIRTUAL tag is set to YES, documented private virtual +# methods of a class will be included in the documentation. +# The default value is: NO. + +EXTRACT_PRIV_VIRTUAL = NO + +# If the EXTRACT_PACKAGE tag is set to YES, all members with package or internal +# scope will be included in the documentation. +# The default value is: NO. + +EXTRACT_PACKAGE = NO + +# If the EXTRACT_STATIC tag is set to YES, all static members of a file will be +# included in the documentation. +# The default value is: NO. + +EXTRACT_STATIC = NO + +# If the EXTRACT_LOCAL_CLASSES tag is set to YES, classes (and structs) defined +# locally in source files will be included in the documentation. If set to NO, +# only classes defined in header files are included. Does not have any effect +# for Java sources. +# The default value is: YES. + +EXTRACT_LOCAL_CLASSES = YES + +# This flag is only useful for Objective-C code. If set to YES, local methods, +# which are defined in the implementation section but not in the interface are +# included in the documentation. If set to NO, only methods in the interface are +# included. +# The default value is: NO. + +EXTRACT_LOCAL_METHODS = NO + +# If this flag is set to YES, the members of anonymous namespaces will be +# extracted and appear in the documentation as a namespace called +# 'anonymous_namespace{file}', where file will be replaced with the base name of +# the file that contains the anonymous namespace. By default anonymous namespace +# are hidden. +# The default value is: NO. + +EXTRACT_ANON_NSPACES = NO + +# If this flag is set to YES, the name of an unnamed parameter in a declaration +# will be determined by the corresponding definition. By default unnamed +# parameters remain unnamed in the output. +# The default value is: YES. + +RESOLVE_UNNAMED_PARAMS = YES + +# If the HIDE_UNDOC_MEMBERS tag is set to YES, doxygen will hide all +# undocumented members inside documented classes or files. If set to NO these +# members will be included in the various overviews, but no documentation +# section is generated. This option has no effect if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_MEMBERS = NO + +# If the HIDE_UNDOC_CLASSES tag is set to YES, doxygen will hide all +# undocumented classes that are normally visible in the class hierarchy. If set +# to NO, these classes will be included in the various overviews. This option +# will also hide undocumented C++ concepts if enabled. This option has no effect +# if EXTRACT_ALL is enabled. +# The default value is: NO. + +HIDE_UNDOC_CLASSES = NO + +# If the HIDE_FRIEND_COMPOUNDS tag is set to YES, doxygen will hide all friend +# declarations. If set to NO, these declarations will be included in the +# documentation. +# The default value is: NO. + +HIDE_FRIEND_COMPOUNDS = NO + +# If the HIDE_IN_BODY_DOCS tag is set to YES, doxygen will hide any +# documentation blocks found inside the body of a function. If set to NO, these +# blocks will be appended to the function's detailed documentation block. +# The default value is: NO. + +HIDE_IN_BODY_DOCS = NO + +# The INTERNAL_DOCS tag determines if documentation that is typed after a +# \internal command is included. If the tag is set to NO then the documentation +# will be excluded. Set it to YES to include the internal documentation. +# The default value is: NO. + +INTERNAL_DOCS = NO + +# With the correct setting of option CASE_SENSE_NAMES doxygen will better be +# able to match the capabilities of the underlying filesystem. In case the +# filesystem is case sensitive (i.e. it supports files in the same directory +# whose names only differ in casing), the option must be set to YES to properly +# deal with such files in case they appear in the input. For filesystems that +# are not case sensitive the option should be set to NO to properly deal with +# output files written for symbols that only differ in casing, such as for two +# classes, one named CLASS and the other named Class, and to also support +# references to files without having to specify the exact matching casing. On +# Windows (including Cygwin) and MacOS, users should typically set this option +# to NO, whereas on Linux or other Unix flavors it should typically be set to +# YES. +# Possible values are: SYSTEM, NO and YES. +# The default value is: SYSTEM. + +CASE_SENSE_NAMES = SYSTEM + +# If the HIDE_SCOPE_NAMES tag is set to NO then doxygen will show members with +# their full class and namespace scopes in the documentation. If set to YES, the +# scope will be hidden. +# The default value is: NO. + +HIDE_SCOPE_NAMES = YES + +# If the HIDE_COMPOUND_REFERENCE tag is set to NO (default) then doxygen will +# append additional text to a page's title, such as Class Reference. If set to +# YES the compound reference will be hidden. +# The default value is: NO. + +HIDE_COMPOUND_REFERENCE= NO + +# If the SHOW_HEADERFILE tag is set to YES then the documentation for a class +# will show which file needs to be included to use the class. +# The default value is: YES. + +SHOW_HEADERFILE = YES + +# If the SHOW_INCLUDE_FILES tag is set to YES then doxygen will put a list of +# the files that are included by a file in the documentation of that file. +# The default value is: YES. + +SHOW_INCLUDE_FILES = YES + +# If the SHOW_GROUPED_MEMB_INC tag is set to YES then Doxygen will add for each +# grouped member an include statement to the documentation, telling the reader +# which file to include in order to use the member. +# The default value is: NO. + +SHOW_GROUPED_MEMB_INC = NO + +# If the FORCE_LOCAL_INCLUDES tag is set to YES then doxygen will list include +# files with double quotes in the documentation rather than with sharp brackets. +# The default value is: NO. + +FORCE_LOCAL_INCLUDES = NO + +# If the INLINE_INFO tag is set to YES then a tag [inline] is inserted in the +# documentation for inline members. +# The default value is: YES. + +INLINE_INFO = YES + +# If the SORT_MEMBER_DOCS tag is set to YES then doxygen will sort the +# (detailed) documentation of file and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. +# The default value is: YES. + +SORT_MEMBER_DOCS = YES + +# If the SORT_BRIEF_DOCS tag is set to YES then doxygen will sort the brief +# descriptions of file, namespace and class members alphabetically by member +# name. If set to NO, the members will appear in declaration order. Note that +# this will also influence the order of the classes in the class list. +# The default value is: NO. + +SORT_BRIEF_DOCS = NO + +# If the SORT_MEMBERS_CTORS_1ST tag is set to YES then doxygen will sort the +# (brief and detailed) documentation of class members so that constructors and +# destructors are listed first. If set to NO the constructors will appear in the +# respective orders defined by SORT_BRIEF_DOCS and SORT_MEMBER_DOCS. +# Note: If SORT_BRIEF_DOCS is set to NO this option is ignored for sorting brief +# member documentation. +# Note: If SORT_MEMBER_DOCS is set to NO this option is ignored for sorting +# detailed member documentation. +# The default value is: NO. + +SORT_MEMBERS_CTORS_1ST = NO + +# If the SORT_GROUP_NAMES tag is set to YES then doxygen will sort the hierarchy +# of group names into alphabetical order. If set to NO the group names will +# appear in their defined order. +# The default value is: NO. + +SORT_GROUP_NAMES = NO + +# If the SORT_BY_SCOPE_NAME tag is set to YES, the class list will be sorted by +# fully-qualified names, including namespaces. If set to NO, the class list will +# be sorted only by class name, not including the namespace part. +# Note: This option is not very useful if HIDE_SCOPE_NAMES is set to YES. +# Note: This option applies only to the class list, not to the alphabetical +# list. +# The default value is: NO. + +SORT_BY_SCOPE_NAME = NO + +# If the STRICT_PROTO_MATCHING option is enabled and doxygen fails to do proper +# type resolution of all parameters of a function it will reject a match between +# the prototype and the implementation of a member function even if there is +# only one candidate or it is obvious which candidate to choose by doing a +# simple string match. By disabling STRICT_PROTO_MATCHING doxygen will still +# accept a match between prototype and implementation in such cases. +# The default value is: NO. + +STRICT_PROTO_MATCHING = NO + +# The GENERATE_TODOLIST tag can be used to enable (YES) or disable (NO) the todo +# list. This list is created by putting \todo commands in the documentation. +# The default value is: YES. + +GENERATE_TODOLIST = YES + +# The GENERATE_TESTLIST tag can be used to enable (YES) or disable (NO) the test +# list. This list is created by putting \test commands in the documentation. +# The default value is: YES. + +GENERATE_TESTLIST = YES + +# The GENERATE_BUGLIST tag can be used to enable (YES) or disable (NO) the bug +# list. This list is created by putting \bug commands in the documentation. +# The default value is: YES. + +GENERATE_BUGLIST = YES + +# The GENERATE_DEPRECATEDLIST tag can be used to enable (YES) or disable (NO) +# the deprecated list. This list is created by putting \deprecated commands in +# the documentation. +# The default value is: YES. + +GENERATE_DEPRECATEDLIST= YES + +# The ENABLED_SECTIONS tag can be used to enable conditional documentation +# sections, marked by \if ... \endif and \cond +# ... \endcond blocks. + +ENABLED_SECTIONS = + +# The MAX_INITIALIZER_LINES tag determines the maximum number of lines that the +# initial value of a variable or macro / define can have for it to appear in the +# documentation. If the initializer consists of more lines than specified here +# it will be hidden. Use a value of 0 to hide initializers completely. The +# appearance of the value of individual variables and macros / defines can be +# controlled using \showinitializer or \hideinitializer command in the +# documentation regardless of this setting. +# Minimum value: 0, maximum value: 10000, default value: 30. + +MAX_INITIALIZER_LINES = 30 + +# Set the SHOW_USED_FILES tag to NO to disable the list of files generated at +# the bottom of the documentation of classes and structs. If set to YES, the +# list will mention the files that were used to generate the documentation. +# The default value is: YES. + +SHOW_USED_FILES = YES + +# Set the SHOW_FILES tag to NO to disable the generation of the Files page. This +# will remove the Files entry from the Quick Index and from the Folder Tree View +# (if specified). +# The default value is: YES. + +SHOW_FILES = YES + +# Set the SHOW_NAMESPACES tag to NO to disable the generation of the Namespaces +# page. This will remove the Namespaces entry from the Quick Index and from the +# Folder Tree View (if specified). +# The default value is: YES. + +SHOW_NAMESPACES = YES + +# The FILE_VERSION_FILTER tag can be used to specify a program or script that +# doxygen should invoke to get the current version for each file (typically from +# the version control system). Doxygen will invoke the program by executing (via +# popen()) the command command input-file, where command is the value of the +# FILE_VERSION_FILTER tag, and input-file is the name of an input file provided +# by doxygen. Whatever the program writes to standard output is used as the file +# version. For an example see the documentation. + +FILE_VERSION_FILTER = + +# The LAYOUT_FILE tag can be used to specify a layout file which will be parsed +# by doxygen. The layout file controls the global structure of the generated +# output files in an output format independent way. To create the layout file +# that represents doxygen's defaults, run doxygen with the -l option. You can +# optionally specify a file name after the option, if omitted DoxygenLayout.xml +# will be used as the name of the layout file. See also section "Changing the +# layout of pages" for information. +# +# Note that if you run doxygen from a directory containing a file called +# DoxygenLayout.xml, doxygen will parse it automatically even if the LAYOUT_FILE +# tag is left empty. + +LAYOUT_FILE = + +# The CITE_BIB_FILES tag can be used to specify one or more bib files containing +# the reference definitions. This must be a list of .bib files. The .bib +# extension is automatically appended if omitted. This requires the bibtex tool +# to be installed. See also https://en.wikipedia.org/wiki/BibTeX for more info. +# For LaTeX the style of the bibliography can be controlled using +# LATEX_BIB_STYLE. To use this feature you need bibtex and perl available in the +# search path. See also \cite for info how to create references. + +CITE_BIB_FILES = + +#--------------------------------------------------------------------------- +# Configuration options related to warning and progress messages +#--------------------------------------------------------------------------- + +# The QUIET tag can be used to turn on/off the messages that are generated to +# standard output by doxygen. If QUIET is set to YES this implies that the +# messages are off. +# The default value is: NO. + +QUIET = NO + +# The WARNINGS tag can be used to turn on/off the warning messages that are +# generated to standard error (stderr) by doxygen. If WARNINGS is set to YES +# this implies that the warnings are on. +# +# Tip: Turn warnings on while writing the documentation. +# The default value is: YES. + +WARNINGS = YES + +# If the WARN_IF_UNDOCUMENTED tag is set to YES then doxygen will generate +# warnings for undocumented members. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: YES. + +WARN_IF_UNDOCUMENTED = YES + +# If the WARN_IF_DOC_ERROR tag is set to YES, doxygen will generate warnings for +# potential errors in the documentation, such as documenting some parameters in +# a documented function twice, or documenting parameters that don't exist or +# using markup commands wrongly. +# The default value is: YES. + +WARN_IF_DOC_ERROR = YES + +# If WARN_IF_INCOMPLETE_DOC is set to YES, doxygen will warn about incomplete +# function parameter documentation. If set to NO, doxygen will accept that some +# parameters have no documentation without warning. +# The default value is: YES. + +WARN_IF_INCOMPLETE_DOC = YES + +# This WARN_NO_PARAMDOC option can be enabled to get warnings for functions that +# are documented, but have no documentation for their parameters or return +# value. If set to NO, doxygen will only warn about wrong parameter +# documentation, but not about the absence of documentation. If EXTRACT_ALL is +# set to YES then this flag will automatically be disabled. See also +# WARN_IF_INCOMPLETE_DOC +# The default value is: NO. + +WARN_NO_PARAMDOC = NO + +# If WARN_IF_UNDOC_ENUM_VAL option is set to YES, doxygen will warn about +# undocumented enumeration values. If set to NO, doxygen will accept +# undocumented enumeration values. If EXTRACT_ALL is set to YES then this flag +# will automatically be disabled. +# The default value is: NO. + +WARN_IF_UNDOC_ENUM_VAL = NO + +# If the WARN_AS_ERROR tag is set to YES then doxygen will immediately stop when +# a warning is encountered. If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS +# then doxygen will continue running as if WARN_AS_ERROR tag is set to NO, but +# at the end of the doxygen process doxygen will return with a non-zero status. +# If the WARN_AS_ERROR tag is set to FAIL_ON_WARNINGS_PRINT then doxygen behaves +# like FAIL_ON_WARNINGS but in case no WARN_LOGFILE is defined doxygen will not +# write the warning messages in between other messages but write them at the end +# of a run, in case a WARN_LOGFILE is defined the warning messages will be +# besides being in the defined file also be shown at the end of a run, unless +# the WARN_LOGFILE is defined as - i.e. standard output (stdout) in that case +# the behavior will remain as with the setting FAIL_ON_WARNINGS. +# Possible values are: NO, YES, FAIL_ON_WARNINGS and FAIL_ON_WARNINGS_PRINT. +# The default value is: NO. + +WARN_AS_ERROR = NO + +# The WARN_FORMAT tag determines the format of the warning messages that doxygen +# can produce. The string should contain the $file, $line, and $text tags, which +# will be replaced by the file and line number from which the warning originated +# and the warning text. Optionally the format may contain $version, which will +# be replaced by the version of the file (if it could be obtained via +# FILE_VERSION_FILTER) +# See also: WARN_LINE_FORMAT +# The default value is: $file:$line: $text. + +WARN_FORMAT = "$file:$line: $text" + +# In the $text part of the WARN_FORMAT command it is possible that a reference +# to a more specific place is given. To make it easier to jump to this place +# (outside of doxygen) the user can define a custom "cut" / "paste" string. +# Example: +# WARN_LINE_FORMAT = "'vi $file +$line'" +# See also: WARN_FORMAT +# The default value is: at line $line of file $file. + +WARN_LINE_FORMAT = "at line $line of file $file" + +# The WARN_LOGFILE tag can be used to specify a file to which warning and error +# messages should be written. If left blank the output is written to standard +# error (stderr). In case the file specified cannot be opened for writing the +# warning and error messages are written to standard error. When as file - is +# specified the warning and error messages are written to standard output +# (stdout). + +WARN_LOGFILE = + +#--------------------------------------------------------------------------- +# Configuration options related to the input files +#--------------------------------------------------------------------------- + +# The INPUT tag is used to specify the files and/or directories that contain +# documented source files. You may enter file names like myfile.cpp or +# directories like /usr/src/myproject. Separate the files or directories with +# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING +# Note: If this tag is empty the current directory is searched. + +INPUT = .. + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses +# libiconv (or the iconv built into libc) for the transcoding. See the libiconv +# documentation (see: +# https://www.gnu.org/software/libiconv/) for the list of possible encodings. +# See also: INPUT_FILE_ENCODING +# The default value is: UTF-8. + +INPUT_ENCODING = UTF-8 + +# This tag can be used to specify the character encoding of the source files +# that doxygen parses The INPUT_FILE_ENCODING tag can be used to specify +# character encoding on a per file pattern basis. Doxygen will compare the file +# name with each pattern and apply the encoding instead of the default +# INPUT_ENCODING) if there is a match. The character encodings are a list of the +# form: pattern=encoding (like *.php=ISO-8859-1). See cfg_input_encoding +# "INPUT_ENCODING" for further information on supported encodings. + +INPUT_FILE_ENCODING = + +# If the value of the INPUT tag contains directories, you can use the +# FILE_PATTERNS tag to specify one or more wildcard patterns (like *.cpp and +# *.h) to filter out the source-files in the directories. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# read by doxygen. +# +# Note the list of default checked file patterns might differ from the list of +# default file extension mappings. +# +# If left blank the following patterns are tested:*.c, *.cc, *.cxx, *.cxxm, +# *.cpp, *.cppm, *.ccm, *.c++, *.c++m, *.java, *.ii, *.ixx, *.ipp, *.i++, *.inl, +# *.idl, *.ddl, *.odl, *.h, *.hh, *.hxx, *.hpp, *.h++, *.ixx, *.l, *.cs, *.d, +# *.php, *.php4, *.php5, *.phtml, *.inc, *.m, *.markdown, *.md, *.mm, *.dox (to +# be provided as doxygen C comment), *.py, *.pyw, *.f90, *.f95, *.f03, *.f08, +# *.f18, *.f, *.for, *.vhd, *.vhdl, *.ucf, *.qsf and *.ice. + +FILE_PATTERNS = *.c \ + *.cc \ + *.cxx \ + *.cxxm \ + *.cpp \ + *.cppm \ + *.ccm \ + *.c++ \ + *.c++m \ + *.java \ + *.ii \ + *.ixx \ + *.ipp \ + *.i++ \ + *.inl \ + *.idl \ + *.ddl \ + *.odl \ + *.h \ + *.hh \ + *.hxx \ + *.hpp \ + *.h++ \ + *.ixx \ + *.l \ + *.cs \ + *.d \ + *.php \ + *.php4 \ + *.php5 \ + *.phtml \ + *.inc \ + *.m \ + *.markdown \ + *.md \ + *.mm \ + *.dox \ + *.py \ + *.pyw \ + *.f90 \ + *.f95 \ + *.f03 \ + *.f08 \ + *.f18 \ + *.f \ + *.for \ + *.vhd \ + *.vhdl \ + *.ucf \ + *.qsf \ + *.ice + +# The RECURSIVE tag can be used to specify whether or not subdirectories should +# be searched for input files as well. +# The default value is: NO. + +RECURSIVE = NO + +# The EXCLUDE tag can be used to specify files and/or directories that should be +# excluded from the INPUT source files. This way you can easily exclude a +# subdirectory from a directory tree whose root is specified with the INPUT tag. +# +# Note that relative paths are relative to the directory from which doxygen is +# run. + +EXCLUDE = + +# The EXCLUDE_SYMLINKS tag can be used to select whether or not files or +# directories that are symbolic links (a Unix file system feature) are excluded +# from the input. +# The default value is: NO. + +EXCLUDE_SYMLINKS = NO + +# If the value of the INPUT tag contains directories, you can use the +# EXCLUDE_PATTERNS tag to specify one or more wildcard patterns to exclude +# certain files from those directories. +# +# Note that the wildcards are matched against the file with absolute path, so to +# exclude all test directories for example use the pattern */test/* + +EXCLUDE_PATTERNS = + +# The EXCLUDE_SYMBOLS tag can be used to specify one or more symbol names +# (namespaces, classes, functions, etc.) that should be excluded from the +# output. The symbol name can be a fully qualified name, a word, or if the +# wildcard * is used, a substring. Examples: ANamespace, AClass, +# ANamespace::AClass, ANamespace::*Test + +EXCLUDE_SYMBOLS = + +# The EXAMPLE_PATH tag can be used to specify one or more files or directories +# that contain example code fragments that are included (see the \include +# command). + +EXAMPLE_PATH = + +# If the value of the EXAMPLE_PATH tag contains directories, you can use the +# EXAMPLE_PATTERNS tag to specify one or more wildcard pattern (like *.cpp and +# *.h) to filter out the source-files in the directories. If left blank all +# files are included. + +EXAMPLE_PATTERNS = * + +# If the EXAMPLE_RECURSIVE tag is set to YES then subdirectories will be +# searched for input files to be used with the \include or \dontinclude commands +# irrespective of the value of the RECURSIVE tag. +# The default value is: NO. + +EXAMPLE_RECURSIVE = NO + +# The IMAGE_PATH tag can be used to specify one or more files or directories +# that contain images that are to be included in the documentation (see the +# \image command). + +IMAGE_PATH = + +# The INPUT_FILTER tag can be used to specify a program that doxygen should +# invoke to filter for each input file. Doxygen will invoke the filter program +# by executing (via popen()) the command: +# +# +# +# where is the value of the INPUT_FILTER tag, and is the +# name of an input file. Doxygen will then use the output that the filter +# program writes to standard output. If FILTER_PATTERNS is specified, this tag +# will be ignored. +# +# Note that the filter must not add or remove lines; it is applied before the +# code is scanned, but not when the output code is generated. If lines are added +# or removed, the anchors will not be placed correctly. +# +# Note that doxygen will use the data processed and written to standard output +# for further processing, therefore nothing else, like debug statements or used +# commands (so in case of a Windows batch file always use @echo OFF), should be +# written to standard output. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +INPUT_FILTER = + +# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern +# basis. Doxygen will compare the file name with each pattern and apply the +# filter if there is a match. The filters are a list of the form: pattern=filter +# (like *.cpp=my_cpp_filter). See INPUT_FILTER for further information on how +# filters are used. If the FILTER_PATTERNS tag is empty or if none of the +# patterns match the file name, INPUT_FILTER is applied. +# +# Note that for custom extensions or not directly supported extensions you also +# need to set EXTENSION_MAPPING for the extension otherwise the files are not +# properly processed by doxygen. + +FILTER_PATTERNS = + +# If the FILTER_SOURCE_FILES tag is set to YES, the input filter (if set using +# INPUT_FILTER) will also be used to filter the input files that are used for +# producing the source files to browse (i.e. when SOURCE_BROWSER is set to YES). +# The default value is: NO. + +FILTER_SOURCE_FILES = NO + +# The FILTER_SOURCE_PATTERNS tag can be used to specify source filters per file +# pattern. A pattern will override the setting for FILTER_PATTERN (if any) and +# it is also possible to disable source filtering for a specific pattern using +# *.ext= (so without naming a filter). +# This tag requires that the tag FILTER_SOURCE_FILES is set to YES. + +FILTER_SOURCE_PATTERNS = + +# If the USE_MDFILE_AS_MAINPAGE tag refers to the name of a markdown file that +# is part of the input, its contents will be placed on the main page +# (index.html). This can be useful if you have a project on for instance GitHub +# and want to reuse the introduction page also for the doxygen output. + +USE_MDFILE_AS_MAINPAGE = + +# The Fortran standard specifies that for fixed formatted Fortran code all +# characters from position 72 are to be considered as comment. A common +# extension is to allow longer lines before the automatic comment starts. The +# setting FORTRAN_COMMENT_AFTER will also make it possible that longer lines can +# be processed before the automatic comment starts. +# Minimum value: 7, maximum value: 10000, default value: 72. + +FORTRAN_COMMENT_AFTER = 72 + +#--------------------------------------------------------------------------- +# Configuration options related to source browsing +#--------------------------------------------------------------------------- + +# If the SOURCE_BROWSER tag is set to YES then a list of source files will be +# generated. Documented entities will be cross-referenced with these sources. +# +# Note: To get rid of all source code in the generated output, make sure that +# also VERBATIM_HEADERS is set to NO. +# The default value is: NO. + +SOURCE_BROWSER = NO + +# Setting the INLINE_SOURCES tag to YES will include the body of functions, +# multi-line macros, enums or list initialized variables directly into the +# documentation. +# The default value is: NO. + +INLINE_SOURCES = NO + +# Setting the STRIP_CODE_COMMENTS tag to YES will instruct doxygen to hide any +# special comment blocks from generated source code fragments. Normal C, C++ and +# Fortran comments will always remain visible. +# The default value is: YES. + +STRIP_CODE_COMMENTS = YES + +# If the REFERENCED_BY_RELATION tag is set to YES then for each documented +# entity all documented functions referencing it will be listed. +# The default value is: NO. + +REFERENCED_BY_RELATION = NO + +# If the REFERENCES_RELATION tag is set to YES then for each documented function +# all documented entities called/used by that function will be listed. +# The default value is: NO. + +REFERENCES_RELATION = NO + +# If the REFERENCES_LINK_SOURCE tag is set to YES and SOURCE_BROWSER tag is set +# to YES then the hyperlinks from functions in REFERENCES_RELATION and +# REFERENCED_BY_RELATION lists will link to the source code. Otherwise they will +# link to the documentation. +# The default value is: YES. + +REFERENCES_LINK_SOURCE = YES + +# If SOURCE_TOOLTIPS is enabled (the default) then hovering a hyperlink in the +# source code will show a tooltip with additional information such as prototype, +# brief description and links to the definition and documentation. Since this +# will make the HTML file larger and loading of large files a bit slower, you +# can opt to disable this feature. +# The default value is: YES. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +SOURCE_TOOLTIPS = YES + +# If the USE_HTAGS tag is set to YES then the references to source code will +# point to the HTML generated by the htags(1) tool instead of doxygen built-in +# source browser. The htags tool is part of GNU's global source tagging system +# (see https://www.gnu.org/software/global/global.html). You will need version +# 4.8.6 or higher. +# +# To use it do the following: +# - Install the latest version of global +# - Enable SOURCE_BROWSER and USE_HTAGS in the configuration file +# - Make sure the INPUT points to the root of the source tree +# - Run doxygen as normal +# +# Doxygen will invoke htags (and that will in turn invoke gtags), so these +# tools must be available from the command line (i.e. in the search path). +# +# The result: instead of the source browser generated by doxygen, the links to +# source code will now point to the output of htags. +# The default value is: NO. +# This tag requires that the tag SOURCE_BROWSER is set to YES. + +USE_HTAGS = NO + +# If the VERBATIM_HEADERS tag is set the YES then doxygen will generate a +# verbatim copy of the header file for each class for which an include is +# specified. Set to NO to disable this. +# See also: Section \class. +# The default value is: YES. + +VERBATIM_HEADERS = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES then doxygen will use the +# clang parser (see: +# http://clang.llvm.org/) for more accurate parsing at the cost of reduced +# performance. This can be particularly helpful with template rich C++ code for +# which doxygen's built-in parser lacks the necessary type information. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. +# The default value is: NO. + +CLANG_ASSISTED_PARSING = NO + +# If the CLANG_ASSISTED_PARSING tag is set to YES and the CLANG_ADD_INC_PATHS +# tag is set to YES then doxygen will add the directory of each input to the +# include path. +# The default value is: YES. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_ADD_INC_PATHS = YES + +# If clang assisted parsing is enabled you can provide the compiler with command +# line options that you would normally use when invoking the compiler. Note that +# the include paths will already be set by doxygen for the files and directories +# specified with INPUT and INCLUDE_PATH. +# This tag requires that the tag CLANG_ASSISTED_PARSING is set to YES. + +CLANG_OPTIONS = + +# If clang assisted parsing is enabled you can provide the clang parser with the +# path to the directory containing a file called compile_commands.json. This +# file is the compilation database (see: +# http://clang.llvm.org/docs/HowToSetupToolingForLLVM.html) containing the +# options used when the source files were built. This is equivalent to +# specifying the -p option to a clang tool, such as clang-check. These options +# will then be passed to the parser. Any options specified with CLANG_OPTIONS +# will be added as well. +# Note: The availability of this option depends on whether or not doxygen was +# generated with the -Duse_libclang=ON option for CMake. + +CLANG_DATABASE_PATH = + +#--------------------------------------------------------------------------- +# Configuration options related to the alphabetical class index +#--------------------------------------------------------------------------- + +# If the ALPHABETICAL_INDEX tag is set to YES, an alphabetical index of all +# compounds will be generated. Enable this if the project contains a lot of +# classes, structs, unions or interfaces. +# The default value is: YES. + +ALPHABETICAL_INDEX = YES + +# The IGNORE_PREFIX tag can be used to specify a prefix (or a list of prefixes) +# that should be ignored while generating the index headers. The IGNORE_PREFIX +# tag works for classes, function and member names. The entity will be placed in +# the alphabetical list under the first letter of the entity name that remains +# after removing the prefix. +# This tag requires that the tag ALPHABETICAL_INDEX is set to YES. + +IGNORE_PREFIX = + +#--------------------------------------------------------------------------- +# Configuration options related to the HTML output +#--------------------------------------------------------------------------- + +# If the GENERATE_HTML tag is set to YES, doxygen will generate HTML output +# The default value is: YES. + +GENERATE_HTML = YES + +# The HTML_OUTPUT tag is used to specify where the HTML docs will be put. If a +# relative path is entered the value of OUTPUT_DIRECTORY will be put in front of +# it. +# The default directory is: html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_OUTPUT = html + +# The HTML_FILE_EXTENSION tag can be used to specify the file extension for each +# generated HTML page (for example: .htm, .php, .asp). +# The default value is: .html. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FILE_EXTENSION = .html + +# The HTML_HEADER tag can be used to specify a user-defined HTML header file for +# each generated HTML page. If the tag is left blank doxygen will generate a +# standard header. +# +# To get valid HTML the header file that includes any scripts and style sheets +# that doxygen needs, which is dependent on the configuration options used (e.g. +# the setting GENERATE_TREEVIEW). It is highly recommended to start with a +# default header using +# doxygen -w html new_header.html new_footer.html new_stylesheet.css +# YourConfigFile +# and then modify the file new_header.html. See also section "Doxygen usage" +# for information on how to generate the default header that doxygen normally +# uses. +# Note: The header is subject to change so you typically have to regenerate the +# default header when upgrading to a newer version of doxygen. For a description +# of the possible markers and block names see the documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_HEADER = + +# The HTML_FOOTER tag can be used to specify a user-defined HTML footer for each +# generated HTML page. If the tag is left blank doxygen will generate a standard +# footer. See HTML_HEADER for more information on how to generate a default +# footer and what special commands can be used inside the footer. See also +# section "Doxygen usage" for information on how to generate the default footer +# that doxygen normally uses. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FOOTER = + +# The HTML_STYLESHEET tag can be used to specify a user-defined cascading style +# sheet that is used by each HTML page. It can be used to fine-tune the look of +# the HTML output. If left blank doxygen will generate a default style sheet. +# See also section "Doxygen usage" for information on how to generate the style +# sheet that doxygen normally uses. +# Note: It is recommended to use HTML_EXTRA_STYLESHEET instead of this tag, as +# it is more robust and this tag (HTML_STYLESHEET) will in the future become +# obsolete. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_STYLESHEET = + +# The HTML_EXTRA_STYLESHEET tag can be used to specify additional user-defined +# cascading style sheets that are included after the standard style sheets +# created by doxygen. Using this option one can overrule certain style aspects. +# This is preferred over using HTML_STYLESHEET since it does not replace the +# standard style sheet and is therefore more robust against future updates. +# Doxygen will copy the style sheet files to the output directory. +# Note: The order of the extra style sheet files is of importance (e.g. the last +# style sheet in the list overrules the setting of the previous ones in the +# list). +# Note: Since the styling of scrollbars can currently not be overruled in +# Webkit/Chromium, the styling will be left out of the default doxygen.css if +# one or more extra stylesheets have been specified. So if scrollbar +# customization is desired it has to be added explicitly. For an example see the +# documentation. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_STYLESHEET = + +# The HTML_EXTRA_FILES tag can be used to specify one or more extra images or +# other source files which should be copied to the HTML output directory. Note +# that these files will be copied to the base HTML output directory. Use the +# $relpath^ marker in the HTML_HEADER and/or HTML_FOOTER files to load these +# files. In the HTML_STYLESHEET file, use the file name only. Also note that the +# files will be copied as-is; there are no commands or markers available. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_EXTRA_FILES = + +# The HTML_COLORSTYLE tag can be used to specify if the generated HTML output +# should be rendered with a dark or light theme. +# Possible values are: LIGHT always generate light mode output, DARK always +# generate dark mode output, AUTO_LIGHT automatically set the mode according to +# the user preference, use light mode if no preference is set (the default), +# AUTO_DARK automatically set the mode according to the user preference, use +# dark mode if no preference is set and TOGGLE allow to user to switch between +# light and dark mode via a button. +# The default value is: AUTO_LIGHT. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE = AUTO_LIGHT + +# The HTML_COLORSTYLE_HUE tag controls the color of the HTML output. Doxygen +# will adjust the colors in the style sheet and background images according to +# this color. Hue is specified as an angle on a color-wheel, see +# https://en.wikipedia.org/wiki/Hue for more information. For instance the value +# 0 represents red, 60 is yellow, 120 is green, 180 is cyan, 240 is blue, 300 +# purple, and 360 is red again. +# Minimum value: 0, maximum value: 359, default value: 220. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_HUE = 220 + +# The HTML_COLORSTYLE_SAT tag controls the purity (or saturation) of the colors +# in the HTML output. For a value of 0 the output will use gray-scales only. A +# value of 255 will produce the most vivid colors. +# Minimum value: 0, maximum value: 255, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_SAT = 100 + +# The HTML_COLORSTYLE_GAMMA tag controls the gamma correction applied to the +# luminance component of the colors in the HTML output. Values below 100 +# gradually make the output lighter, whereas values above 100 make the output +# darker. The value divided by 100 is the actual gamma applied, so 80 represents +# a gamma of 0.8, The value 220 represents a gamma of 2.2, and 100 does not +# change the gamma. +# Minimum value: 40, maximum value: 240, default value: 80. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COLORSTYLE_GAMMA = 80 + +# If the HTML_DYNAMIC_MENUS tag is set to YES then the generated HTML +# documentation will contain a main index with vertical navigation menus that +# are dynamically created via JavaScript. If disabled, the navigation index will +# consists of multiple levels of tabs that are statically embedded in every HTML +# page. Disable this option to support browsers that do not have JavaScript, +# like the Qt help browser. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_MENUS = YES + +# If the HTML_DYNAMIC_SECTIONS tag is set to YES then the generated HTML +# documentation will contain sections that can be hidden and shown after the +# page has loaded. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_DYNAMIC_SECTIONS = NO + +# If the HTML_CODE_FOLDING tag is set to YES then classes and functions can be +# dynamically folded and expanded in the generated HTML source code. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_CODE_FOLDING = YES + +# If the HTML_COPY_CLIPBOARD tag is set to YES then doxygen will show an icon in +# the top right corner of code and text fragments that allows the user to copy +# its content to the clipboard. Note this only works if supported by the browser +# and the web page is served via a secure context (see: +# https://www.w3.org/TR/secure-contexts/), i.e. using the https: or file: +# protocol. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_COPY_CLIPBOARD = YES + +# Doxygen stores a couple of settings persistently in the browser (via e.g. +# cookies). By default these settings apply to all HTML pages generated by +# doxygen across all projects. The HTML_PROJECT_COOKIE tag can be used to store +# the settings under a project specific key, such that the user preferences will +# be stored separately. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_PROJECT_COOKIE = + +# With HTML_INDEX_NUM_ENTRIES one can control the preferred number of entries +# shown in the various tree structured indices initially; the user can expand +# and collapse entries dynamically later on. Doxygen will expand the tree to +# such a level that at most the specified number of entries are visible (unless +# a fully collapsed tree already exceeds this amount). So setting the number of +# entries 1 will produce a full collapsed tree by default. 0 is a special value +# representing an infinite number of entries and will result in a full expanded +# tree by default. +# Minimum value: 0, maximum value: 9999, default value: 100. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_INDEX_NUM_ENTRIES = 100 + +# If the GENERATE_DOCSET tag is set to YES, additional index files will be +# generated that can be used as input for Apple's Xcode 3 integrated development +# environment (see: +# https://developer.apple.com/xcode/), introduced with OSX 10.5 (Leopard). To +# create a documentation set, doxygen will generate a Makefile in the HTML +# output directory. Running make will produce the docset in that directory and +# running make install will install the docset in +# ~/Library/Developer/Shared/Documentation/DocSets so that Xcode will find it at +# startup. See https://developer.apple.com/library/archive/featuredarticles/Doxy +# genXcode/_index.html for more information. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_DOCSET = NO + +# This tag determines the name of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# The default value is: Doxygen generated docs. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDNAME = "Doxygen generated docs" + +# This tag determines the URL of the docset feed. A documentation feed provides +# an umbrella under which multiple documentation sets from a single provider +# (such as a company or product suite) can be grouped. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_FEEDURL = + +# This tag specifies a string that should uniquely identify the documentation +# set bundle. This should be a reverse domain-name style string, e.g. +# com.mycompany.MyDocSet. Doxygen will append .docset to the name. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_BUNDLE_ID = org.doxygen.Project + +# The DOCSET_PUBLISHER_ID tag specifies a string that should uniquely identify +# the documentation publisher. This should be a reverse domain-name style +# string, e.g. com.mycompany.MyDocSet.documentation. +# The default value is: org.doxygen.Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_ID = org.doxygen.Publisher + +# The DOCSET_PUBLISHER_NAME tag identifies the documentation publisher. +# The default value is: Publisher. +# This tag requires that the tag GENERATE_DOCSET is set to YES. + +DOCSET_PUBLISHER_NAME = Publisher + +# If the GENERATE_HTMLHELP tag is set to YES then doxygen generates three +# additional HTML index files: index.hhp, index.hhc, and index.hhk. The +# index.hhp is a project file that can be read by Microsoft's HTML Help Workshop +# on Windows. In the beginning of 2021 Microsoft took the original page, with +# a.o. the download links, offline the HTML help workshop was already many years +# in maintenance mode). You can download the HTML help workshop from the web +# archives at Installation executable (see: +# http://web.archive.org/web/20160201063255/http://download.microsoft.com/downlo +# ad/0/A/9/0A939EF6-E31C-430F-A3DF-DFAE7960D564/htmlhelp.exe). +# +# The HTML Help Workshop contains a compiler that can convert all HTML output +# generated by doxygen into a single compiled HTML file (.chm). Compiled HTML +# files are now used as the Windows 98 help format, and will replace the old +# Windows help format (.hlp) on all Windows platforms in the future. Compressed +# HTML files also contain an index, a table of contents, and you can search for +# words in the documentation. The HTML workshop also contains a viewer for +# compressed HTML files. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_HTMLHELP = NO + +# The CHM_FILE tag can be used to specify the file name of the resulting .chm +# file. You can add a path in front of the file if the result should not be +# written to the html output directory. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_FILE = + +# The HHC_LOCATION tag can be used to specify the location (absolute path +# including file name) of the HTML help compiler (hhc.exe). If non-empty, +# doxygen will try to run the HTML help compiler on the generated index.hhp. +# The file has to be specified with full path. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +HHC_LOCATION = + +# The GENERATE_CHI flag controls if a separate .chi index file is generated +# (YES) or that it should be included in the main .chm file (NO). +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +GENERATE_CHI = NO + +# The CHM_INDEX_ENCODING is used to encode HtmlHelp index (hhk), content (hhc) +# and project file content. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +CHM_INDEX_ENCODING = + +# The BINARY_TOC flag controls whether a binary table of contents is generated +# (YES) or a normal table of contents (NO) in the .chm file. Furthermore it +# enables the Previous and Next buttons. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +BINARY_TOC = NO + +# The TOC_EXPAND flag can be set to YES to add extra items for group members to +# the table of contents of the HTML help documentation and to the tree view. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTMLHELP is set to YES. + +TOC_EXPAND = NO + +# The SITEMAP_URL tag is used to specify the full URL of the place where the +# generated documentation will be placed on the server by the user during the +# deployment of the documentation. The generated sitemap is called sitemap.xml +# and placed on the directory specified by HTML_OUTPUT. In case no SITEMAP_URL +# is specified no sitemap is generated. For information about the sitemap +# protocol see https://www.sitemaps.org +# This tag requires that the tag GENERATE_HTML is set to YES. + +SITEMAP_URL = + +# If the GENERATE_QHP tag is set to YES and both QHP_NAMESPACE and +# QHP_VIRTUAL_FOLDER are set, an additional index file will be generated that +# can be used as input for Qt's qhelpgenerator to generate a Qt Compressed Help +# (.qch) of the generated HTML documentation. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_QHP = NO + +# If the QHG_LOCATION tag is specified, the QCH_FILE tag can be used to specify +# the file name of the resulting .qch file. The path specified is relative to +# the HTML output folder. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QCH_FILE = + +# The QHP_NAMESPACE tag specifies the namespace to use when generating Qt Help +# Project output. For more information please see Qt Help Project / Namespace +# (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#namespace). +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_NAMESPACE = org.doxygen.Project + +# The QHP_VIRTUAL_FOLDER tag specifies the namespace to use when generating Qt +# Help Project output. For more information please see Qt Help Project / Virtual +# Folders (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#virtual-folders). +# The default value is: doc. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_VIRTUAL_FOLDER = doc + +# If the QHP_CUST_FILTER_NAME tag is set, it specifies the name of a custom +# filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_NAME = + +# The QHP_CUST_FILTER_ATTRS tag specifies the list of the attributes of the +# custom filter to add. For more information please see Qt Help Project / Custom +# Filters (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#custom-filters). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_CUST_FILTER_ATTRS = + +# The QHP_SECT_FILTER_ATTRS tag specifies the list of the attributes this +# project's filter section matches. Qt Help Project / Filter Attributes (see: +# https://doc.qt.io/archives/qt-4.8/qthelpproject.html#filter-attributes). +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHP_SECT_FILTER_ATTRS = + +# The QHG_LOCATION tag can be used to specify the location (absolute path +# including file name) of Qt's qhelpgenerator. If non-empty doxygen will try to +# run qhelpgenerator on the generated .qhp file. +# This tag requires that the tag GENERATE_QHP is set to YES. + +QHG_LOCATION = + +# If the GENERATE_ECLIPSEHELP tag is set to YES, additional index files will be +# generated, together with the HTML files, they form an Eclipse help plugin. To +# install this plugin and make it available under the help contents menu in +# Eclipse, the contents of the directory containing the HTML and XML files needs +# to be copied into the plugins directory of eclipse. The name of the directory +# within the plugins directory should be the same as the ECLIPSE_DOC_ID value. +# After copying Eclipse needs to be restarted before the help appears. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_ECLIPSEHELP = NO + +# A unique identifier for the Eclipse help plugin. When installing the plugin +# the directory name containing the HTML and XML files should also have this +# name. Each documentation set should have its own identifier. +# The default value is: org.doxygen.Project. +# This tag requires that the tag GENERATE_ECLIPSEHELP is set to YES. + +ECLIPSE_DOC_ID = org.doxygen.Project + +# If you want full control over the layout of the generated HTML pages it might +# be necessary to disable the index and replace it with your own. The +# DISABLE_INDEX tag can be used to turn on/off the condensed index (tabs) at top +# of each HTML page. A value of NO enables the index and the value YES disables +# it. Since the tabs in the index contain the same information as the navigation +# tree, you can set this option to YES if you also set GENERATE_TREEVIEW to YES. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +DISABLE_INDEX = NO + +# The GENERATE_TREEVIEW tag is used to specify whether a tree-like index +# structure should be generated to display hierarchical information. If the tag +# value is set to YES, a side panel will be generated containing a tree-like +# index structure (just like the one that is generated for HTML Help). For this +# to work a browser that supports JavaScript, DHTML, CSS and frames is required +# (i.e. any modern browser). Windows users are probably better off using the +# HTML help feature. Via custom style sheets (see HTML_EXTRA_STYLESHEET) one can +# further fine tune the look of the index (see "Fine-tuning the output"). As an +# example, the default style sheet generated by doxygen has an example that +# shows how to put an image at the root of the tree instead of the PROJECT_NAME. +# Since the tree basically has the same information as the tab index, you could +# consider setting DISABLE_INDEX to YES when enabling this option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +GENERATE_TREEVIEW = YES + +# When both GENERATE_TREEVIEW and DISABLE_INDEX are set to YES, then the +# FULL_SIDEBAR option determines if the side bar is limited to only the treeview +# area (value NO) or if it should extend to the full height of the window (value +# YES). Setting this to YES gives a layout similar to +# https://docs.readthedocs.io with more room for contents, but less room for the +# project logo, title, and description. If either GENERATE_TREEVIEW or +# DISABLE_INDEX is set to NO, this option has no effect. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FULL_SIDEBAR = NO + +# The ENUM_VALUES_PER_LINE tag can be used to set the number of enum values that +# doxygen will group on one line in the generated HTML documentation. +# +# Note that a value of 0 will completely suppress the enum values from appearing +# in the overview section. +# Minimum value: 0, maximum value: 20, default value: 4. +# This tag requires that the tag GENERATE_HTML is set to YES. + +ENUM_VALUES_PER_LINE = 4 + +# If the treeview is enabled (see GENERATE_TREEVIEW) then this tag can be used +# to set the initial width (in pixels) of the frame in which the tree is shown. +# Minimum value: 0, maximum value: 1500, default value: 250. +# This tag requires that the tag GENERATE_HTML is set to YES. + +TREEVIEW_WIDTH = 250 + +# If the EXT_LINKS_IN_WINDOW option is set to YES, doxygen will open links to +# external symbols imported via tag files in a separate window. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +EXT_LINKS_IN_WINDOW = NO + +# If the OBFUSCATE_EMAILS tag is set to YES, doxygen will obfuscate email +# addresses. +# The default value is: YES. +# This tag requires that the tag GENERATE_HTML is set to YES. + +OBFUSCATE_EMAILS = YES + +# If the HTML_FORMULA_FORMAT option is set to svg, doxygen will use the pdf2svg +# tool (see https://github.com/dawbarton/pdf2svg) or inkscape (see +# https://inkscape.org) to generate formulas as SVG images instead of PNGs for +# the HTML output. These images will generally look nicer at scaled resolutions. +# Possible values are: png (the default) and svg (looks nicer but requires the +# pdf2svg or inkscape tool). +# The default value is: png. +# This tag requires that the tag GENERATE_HTML is set to YES. + +HTML_FORMULA_FORMAT = png + +# Use this tag to change the font size of LaTeX formulas included as images in +# the HTML documentation. When you change the font size after a successful +# doxygen run you need to manually remove any form_*.png images from the HTML +# output directory to force them to be regenerated. +# Minimum value: 8, maximum value: 50, default value: 10. +# This tag requires that the tag GENERATE_HTML is set to YES. + +FORMULA_FONTSIZE = 10 + +# The FORMULA_MACROFILE can contain LaTeX \newcommand and \renewcommand commands +# to create new LaTeX commands to be used in formulas as building blocks. See +# the section "Including formulas" for details. + +FORMULA_MACROFILE = + +# Enable the USE_MATHJAX option to render LaTeX formulas using MathJax (see +# https://www.mathjax.org) which uses client side JavaScript for the rendering +# instead of using pre-rendered bitmaps. Use this if you do not have LaTeX +# installed or if you want to formulas look prettier in the HTML output. When +# enabled you may also need to install MathJax separately and configure the path +# to it using the MATHJAX_RELPATH option. +# The default value is: NO. +# This tag requires that the tag GENERATE_HTML is set to YES. + +USE_MATHJAX = NO + +# With MATHJAX_VERSION it is possible to specify the MathJax version to be used. +# Note that the different versions of MathJax have different requirements with +# regards to the different settings, so it is possible that also other MathJax +# settings have to be changed when switching between the different MathJax +# versions. +# Possible values are: MathJax_2 and MathJax_3. +# The default value is: MathJax_2. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_VERSION = MathJax_2 + +# When MathJax is enabled you can set the default output format to be used for +# the MathJax output. For more details about the output format see MathJax +# version 2 (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) and MathJax version 3 +# (see: +# http://docs.mathjax.org/en/latest/web/components/output.html). +# Possible values are: HTML-CSS (which is slower, but has the best +# compatibility. This is the name for Mathjax version 2, for MathJax version 3 +# this will be translated into chtml), NativeMML (i.e. MathML. Only supported +# for NathJax 2. For MathJax version 3 chtml will be used instead.), chtml (This +# is the name for Mathjax version 3, for MathJax version 2 this will be +# translated into HTML-CSS) and SVG. +# The default value is: HTML-CSS. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_FORMAT = HTML-CSS + +# When MathJax is enabled you need to specify the location relative to the HTML +# output directory using the MATHJAX_RELPATH option. The destination directory +# should contain the MathJax.js script. For instance, if the mathjax directory +# is located at the same level as the HTML output directory, then +# MATHJAX_RELPATH should be ../mathjax. The default value points to the MathJax +# Content Delivery Network so you can quickly see the result without installing +# MathJax. However, it is strongly recommended to install a local copy of +# MathJax from https://www.mathjax.org before deployment. The default value is: +# - in case of MathJax version 2: https://cdn.jsdelivr.net/npm/mathjax@2 +# - in case of MathJax version 3: https://cdn.jsdelivr.net/npm/mathjax@3 +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_RELPATH = + +# The MATHJAX_EXTENSIONS tag can be used to specify one or more MathJax +# extension names that should be enabled during MathJax rendering. For example +# for MathJax version 2 (see +# https://docs.mathjax.org/en/v2.7-latest/tex.html#tex-and-latex-extensions): +# MATHJAX_EXTENSIONS = TeX/AMSmath TeX/AMSsymbols +# For example for MathJax version 3 (see +# http://docs.mathjax.org/en/latest/input/tex/extensions/index.html): +# MATHJAX_EXTENSIONS = ams +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_EXTENSIONS = + +# The MATHJAX_CODEFILE tag can be used to specify a file with javascript pieces +# of code that will be used on startup of the MathJax code. See the MathJax site +# (see: +# http://docs.mathjax.org/en/v2.7-latest/output.html) for more details. For an +# example see the documentation. +# This tag requires that the tag USE_MATHJAX is set to YES. + +MATHJAX_CODEFILE = + +# When the SEARCHENGINE tag is enabled doxygen will generate a search box for +# the HTML output. The underlying search engine uses javascript and DHTML and +# should work on any modern browser. Note that when using HTML help +# (GENERATE_HTMLHELP), Qt help (GENERATE_QHP), or docsets (GENERATE_DOCSET) +# there is already a search function so this one should typically be disabled. +# For large projects the javascript based search engine can be slow, then +# enabling SERVER_BASED_SEARCH may provide a better solution. It is possible to +# search using the keyboard; to jump to the search box use + S +# (what the is depends on the OS and browser, but it is typically +# , /