diff --git a/src/main/build/atomic.h b/src/main/build/atomic.h
index fc4944f302..393ec5ab84 100644
--- a/src/main/build/atomic.h
+++ b/src/main/build/atomic.h
@@ -20,6 +20,12 @@
// only set_BASEPRI is implemented in device library. It does always create memory barrier
// missing versions are implemented here
+#ifdef UNIT_TEST
+static inline void __set_BASEPRI(uint32_t basePri) {(void)basePri;}
+static inline void __set_BASEPRI_MAX(uint32_t basePri) {(void)basePri;}
+static inline void __set_BASEPRI_nb(uint32_t basePri) {(void)basePri;}
+static inline void __set_BASEPRI_MAX_nb(uint32_t basePri) {(void)basePri;}
+#else
// set BASEPRI and BASEPRI_MAX register, but do not create memory barrier
__attribute__( ( always_inline ) ) static inline void __set_BASEPRI_nb(uint32_t basePri)
{
@@ -30,6 +36,7 @@ __attribute__( ( always_inline ) ) static inline void __set_BASEPRI_MAX_nb(uint3
{
__ASM volatile ("\tMSR basepri_max, %0\n" : : "r" (basePri) );
}
+#endif // UNIT_TEST
// cleanup BASEPRI restore function, with global memory barrier
static inline void __basepriRestoreMem(uint8_t *val)
@@ -59,6 +66,10 @@ static inline uint8_t __basepriSetRetVal(uint8_t prio)
// Run block with elevated BASEPRI (using BASEPRI_MAX), restoring BASEPRI on exit. All exit paths are handled
// Full memory barrier is placed at start and exit of block
+#ifdef UNIT_TEST
+#define ATOMIC_BLOCK(prio) {}
+#define ATOMIC_BLOCK_NB(prio) {}
+#else
#define ATOMIC_BLOCK(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestoreMem))) = __get_BASEPRI(), \
__ToDo = __basepriSetMemRetVal(prio); __ToDo ; __ToDo = 0 )
@@ -71,6 +82,8 @@ static inline uint8_t __basepriSetRetVal(uint8_t prio)
#define ATOMIC_BLOCK_NB(prio) for ( uint8_t __basepri_save __attribute__((__cleanup__(__basepriRestore))) = __get_BASEPRI(), \
__ToDo = __basepriSetRetVal(prio); __ToDo ; __ToDo = 0 ) \
+#endif // UNIT_TEST
+
// ATOMIC_BARRIER
// Create memory barrier
// - at the beginning (all data must be reread from memory)
diff --git a/src/main/drivers/system.c b/src/main/drivers/system.c
index e9f7865b1d..1319dea750 100644
--- a/src/main/drivers/system.c
+++ b/src/main/drivers/system.c
@@ -25,6 +25,7 @@
#include "sound_beeper.h"
#include "nvic.h"
#include "build/atomic.h"
+#include "build/build_config.h"
#include "system.h"
@@ -48,12 +49,13 @@ void registerExtiCallbackHandler(IRQn_Type irqn, extiCallbackHandlerFunc *fn)
}
// cycles per microsecond
-static timeUs_t usTicks = 0;
+STATIC_UNIT_TESTED timeUs_t usTicks = 0;
// current uptime for 1kHz systick timer. will rollover after 49 days. hopefully we won't care.
-static volatile timeMs_t sysTickUptime = 0;
+STATIC_UNIT_TESTED volatile timeMs_t sysTickUptime = 0;
// cached value of RCC->CSR
uint32_t cachedRccCsrValue;
+#ifndef UNIT_TEST
void cycleCounterInit(void)
{
#if defined(USE_HAL_DRIVER)
@@ -69,6 +71,7 @@ void cycleCounterInit(void)
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk;
}
+#endif // UNIT_TEST
// SysTick
@@ -89,8 +92,12 @@ void SysTick_Handler(void)
uint32_t ticks(void)
{
+#ifdef UNIT_TEST
+ return 0;
+#else
CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk;
return DWT->CYCCNT;
+#endif
}
timeDelta_t ticks_diff_us(uint32_t begin, uint32_t end)
@@ -98,7 +105,7 @@ timeDelta_t ticks_diff_us(uint32_t begin, uint32_t end)
return (end - begin) / usTicks;
}
-// Return system uptime in microseconds (rollover in 70minutes)
+// Return system uptime in microseconds
timeUs_t microsISR(void)
{
register uint32_t ms, pending, cycle_cnt;
@@ -132,9 +139,11 @@ timeUs_t micros(void)
// Call microsISR() in interrupt and elevated (non-zero) BASEPRI context
+#ifndef UNIT_TEST
if ((SCB->ICSR & SCB_ICSR_VECTACTIVE_Msk) || (__get_BASEPRI())) {
return microsISR();
}
+#endif
do {
ms = sysTickUptime;
@@ -199,6 +208,9 @@ void delay(timeMs_t ms)
void failureMode(failureMode_e mode)
{
+#ifdef UNIT_TEST
+ (void)mode;
+#else
int codeRepeatsRemaining = 10;
int codeFlashesRemaining;
int shortFlashesRemaining;
@@ -239,4 +251,5 @@ void failureMode(failureMode_e mode)
#else
systemResetToBootloader();
#endif
+#endif //UNIT_TEST
}
diff --git a/src/test/Makefile b/src/test/Makefile
index 9a14a52c25..509e65d274 100644
--- a/src/test/Makefile
+++ b/src/test/Makefile
@@ -630,6 +630,29 @@ $(OBJECT_DIR)/sensor_gyro_unittest : \
$(CXX) $(CXX_FLAGS) $^ -o $(OBJECT_DIR)/$@
+$(OBJECT_DIR)/drivers/system.o : \
+ $(USER_DIR)/drivers/system.c \
+ $(USER_DIR)/drivers/system.h \
+ $(GTEST_HEADERS)
+
+ @mkdir -p $(dir $@)
+ $(CC) $(C_FLAGS) $(TEST_CFLAGS) -c $(USER_DIR)/drivers/system.c -o $@
+
+$(OBJECT_DIR)/time_unittest.o : \
+ $(TEST_DIR)/time_unittest.cc \
+ $(USER_DIR)/drivers/system.h \
+ $(GTEST_HEADERS)
+
+ @mkdir -p $(dir $@)
+ $(CXX) $(CXX_FLAGS) $(TEST_CFLAGS) -c $(TEST_DIR)/time_unittest.cc -o $@
+
+$(OBJECT_DIR)/time_unittest : \
+ $(OBJECT_DIR)/drivers/system.o \
+ $(OBJECT_DIR)/time_unittest.o \
+ $(OBJECT_DIR)/gtest_main.a
+
+ $(CXX) $(CXX_FLAGS) $^ -o $(OBJECT_DIR)/$@
+
test: $(TESTS:%=test-%)
diff --git a/src/test/unit/platform.h b/src/test/unit/platform.h
index 50d5eec89c..6b5d64ac6c 100644
--- a/src/test/unit/platform.h
+++ b/src/test/unit/platform.h
@@ -55,6 +55,20 @@ uint8_t DMA_GetFlagStatus(uint32_t);
void DMA_Cmd(DMA_Channel_TypeDef*, FunctionalState );
void DMA_ClearFlag(uint32_t);
+typedef struct
+{
+ uint32_t CTRL; /*!< Offset: 0x000 (R/W) SysTick Control and Status Register */
+ uint32_t LOAD; /*!< Offset: 0x004 (R/W) SysTick Reload Value Register */
+ uint32_t VAL; /*!< Offset: 0x008 (R/W) SysTick Current Value Register */
+ uint32_t CALIB; /*!< Offset: 0x00C (R/ ) SysTick Calibration Register */
+} SysTick_Type;
+
+#define SysTick_CTRL_COUNTFLAG_Pos 16 /*!< SysTick CTRL: COUNTFLAG Position */
+#define SysTick_CTRL_COUNTFLAG_Msk (1UL << SysTick_CTRL_COUNTFLAG_Pos) /*!< SysTick CTRL: COUNTFLAG Mask */
+
+extern SysTick_Type *SysTick;
+
+
#define WS2811_DMA_TC_FLAG 1
#define WS2811_DMA_HANDLER_IDENTIFER 0
diff --git a/src/test/unit/time_unittest.cc b/src/test/unit/time_unittest.cc
new file mode 100644
index 0000000000..2c9f819f2a
--- /dev/null
+++ b/src/test/unit/time_unittest.cc
@@ -0,0 +1,72 @@
+/*
+ * This file is part of Cleanflight.
+ *
+ * Cleanflight is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * Cleanflight is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with Cleanflight. If not, see .
+ */
+
+#include
+#include
+
+#include
+
+extern "C" {
+ #include "common/time.h"
+ #include "drivers/system.h"
+ extern timeUs_t usTicks;
+ extern volatile timeMs_t sysTickUptime;
+}
+
+#include "unittest_macros.h"
+#include "gtest/gtest.h"
+
+SysTick_Type SysTickValue;
+SysTick_Type *SysTick = &SysTickValue;
+
+TEST(TimeUnittest, TestMillis)
+{
+ sysTickUptime = 0;
+ EXPECT_EQ(0, millis());
+ sysTickUptime = 1;
+ EXPECT_EQ(1, millis());
+}
+
+TEST(TimeUnittest, TestMicros)
+{
+ usTicks = 168;
+ SysTick->VAL = 1000 * usTicks;
+ sysTickUptime = 0;
+ EXPECT_EQ(0, micros());
+ sysTickUptime = 1;
+ EXPECT_EQ(1000, micros());
+ // ULONG_MAX = 4294967295
+ sysTickUptime = 429496; // ULONG_MAX / 1000;
+ EXPECT_EQ(429496000, micros());
+ sysTickUptime = 429497;
+ EXPECT_EQ(429497000, micros());
+ sysTickUptime = 500000;
+ EXPECT_EQ(500000000, micros());
+
+ sysTickUptime = 0;
+ SysTick->VAL = 0;
+ EXPECT_EQ(1000, micros());
+ sysTickUptime = 1;
+ EXPECT_EQ(2000, micros());
+ // ULONG_MAX = 4294967295
+ sysTickUptime = 429496; // ULONG_MAX / 1000;
+ EXPECT_EQ(429497000, micros());
+ sysTickUptime = 429497;
+ EXPECT_EQ(429498000, micros());
+ sysTickUptime = 500000;
+ EXPECT_EQ(500001000, micros());
+}