diff --git a/sys/include/div.h b/sys/include/div.h new file mode 100644 index 0000000000000000000000000000000000000000..3b83aad628d62af7d067d5d23f828c415061c9b0 --- /dev/null +++ b/sys/include/div.h @@ -0,0 +1,112 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @brief Provides integer division functions + * + * This header provides some integer division functions that can be used + * to prevent linking in compiler-generated ones, which are often larger. + * + * @file + * @ingroup sys_util + * @author Kaspar Schleiser <kaspar@schleiser.de> + * @{ + */ + +#ifndef DIV_H +#define DIV_H + +#include <assert.h> +#include <stdint.h> + +#ifdef __cplusplus +extern "C" { +#endif + +/** + * @brief Integer divide val by 15625 + * + * @pre val <= 16383999997 + * + * @param[in] val dividend + * @return (val / 15625) + */ +static inline uint64_t div_u64_by_15625(uint64_t val) +{ + /* a higher value would overflow 2^64 in the multiplication that follows */ + assert(val <= 16383999997LLU); + + return (val * 0x431bde83UL) >> (12 + 32); +} + +/** + * @brief Integer divide val by 1000000 + * + * @pre val <= 1048575999808 + * + * @param[in] val dividend + * @return (val / 1000000) + */ +static inline uint64_t div_u64_by_1000000(uint64_t val) +{ + /* a higher value would overflow 2^64 in the multiplication that follows */ + assert(val <= 1048575999808LLU); + + return div_u64_by_15625(val>>6); +} + +/** + * @brief Divide val by (15625/512) + * + * This is used to quantize a 1MHz value to the closest 32768Hz value, + * e.g., for timers. + * + * The algorithm actually multiplies by 512 first, then divides by 15625, + * keeping the result closer to a floored floating point division. + * + * @param[in] val dividend + * @return (val / (15625/512)) + */ +static inline uint32_t div_u32_by_15625div512(uint32_t val) +{ + return ((uint64_t)(val) * 0x431bde83ul) >> (12 + 32 - 9); +} + +/** + * @brief Integer divide val by 10 + * + * @param[in] n dividend + * @return (n / 10) + */ +static inline uint32_t div_u32_by_10(uint32_t n) { + uint32_t q, r; + q = (n >> 1) + (n >> 2); + q = q + (q >> 4); + q = q + (q >> 8); + q = q + (q >> 16); + q = q >> 3; + r = n - (((q << 2) + q) << 1); + return q + (r > 9); +} + +/** + * @brief Modulo 10 + * + * @param[in] n dividend + * @return (n % 10) + */ +static inline uint32_t div_u32_mod_10(uint32_t n) +{ + return n - (div_u32_by_10(n)*10); +} + +#ifdef __cplusplus +} +#endif +/** @} */ +#endif /* DIV_H */ diff --git a/sys/xtimer/xtimer.c b/sys/xtimer/xtimer.c index a03cb55bd96733b467f65a9bec0372f9c3771245..c9993aae2b7280bcd1f70fb6fd3f91409b03d0a8 100644 --- a/sys/xtimer/xtimer.c +++ b/sys/xtimer/xtimer.c @@ -21,6 +21,7 @@ #include "mutex.h" #include "thread.h" #include "irq.h" +#include "div.h" #include "timex.h" @@ -150,21 +151,11 @@ void xtimer_set_wakeup64(xtimer_t *timer, uint64_t offset, kernel_pid_t pid) _xtimer_set64(timer, offset, offset >> 32); } -/** - * see http://www.hackersdelight.org/magic.htm. - * This is to avoid using long integer division functions - * the compiler otherwise links in. - */ -static inline uint64_t _us_to_sec(uint64_t us) -{ - return (unsigned long long)(us * 0x431bde83) >> (0x12 + 32); -} - void xtimer_now_timex(timex_t *out) { uint64_t now = xtimer_now64(); - out->seconds = _us_to_sec(now); + out->seconds = div_u64_by_1000000(now); out->microseconds = now - (out->seconds * SEC_IN_USEC); } diff --git a/tests/unittests/tests-div/Makefile b/tests/unittests/tests-div/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..48422e909a47d7cd428d10fa73825060ccc8d8c2 --- /dev/null +++ b/tests/unittests/tests-div/Makefile @@ -0,0 +1 @@ +include $(RIOTBASE)/Makefile.base diff --git a/tests/unittests/tests-div/tests-div.c b/tests/unittests/tests-div/tests-div.c new file mode 100644 index 0000000000000000000000000000000000000000..824d1952a32dbb87d68094e63360c958d34239c2 --- /dev/null +++ b/tests/unittests/tests-div/tests-div.c @@ -0,0 +1,120 @@ +/* +* Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> +* +* This file is subject to the terms and conditions of the GNU Lesser +* General Public License v2.1. See the file LICENSE in the top level +* directory for more details. +*/ + +#include <string.h> +#include "embUnit.h" +#include "tests-div.h" + +#include "div.h" + +#define ENABLE_DEBUG (0) +#include "debug.h" + +static uint32_t u32_test_values[] = { + 0, + 1, + 10, + 32, + 15625, + 15625LU*5, + (15625LU*5)+1, + 0xffff, + 0xffff<<10, + 0xffffffff +}; + +static uint64_t u64_test_values[] = { + 0xffffffffULL+1 +}; + +#define N_U32_VALS (sizeof(u32_test_values)/sizeof(uint32_t)) +#define N_U64_VALS (sizeof(u64_test_values)/sizeof(uint64_t)) + +static void test_div_u64_by_15625(void) +{ + for (unsigned i = 0; i < N_U32_VALS; i++) { + DEBUG("Dividing %"PRIu32" by 15625...\n", u32_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u64_by_15625(u32_test_values[i]), + u32_test_values[i]/15625); + } + + for (unsigned i = 0; i < N_U64_VALS; i++) { + DEBUG("Dividing %"PRIu64" by 15625...\n", u64_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u64_by_15625(u64_test_values[i]), + u64_test_values[i]/15625); + } +} + +static void test_div_u32_by_15625div512(void) +{ + for (unsigned i = 0; i < N_U32_VALS; i++) { + DEBUG("Dividing %"PRIu32" by (15625/512)...\n", u32_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u32_by_15625div512(u32_test_values[i]), + (uint64_t)u32_test_values[i]*512LU/15625); + } +} + +static void test_div_u64_by_1000000(void) +{ + for (unsigned i = 0; i < N_U32_VALS; i++) { + DEBUG("Dividing %"PRIu32" by 1000000...\n", u32_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u64_by_1000000(u32_test_values[i]), + u32_test_values[i]/1000000); + } + + for (unsigned i = 0; i < N_U64_VALS; i++) { + DEBUG("Dividing %"PRIu64" by 1000000...\n", u64_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u64_by_1000000(u64_test_values[i]), + u64_test_values[i]/1000000U); + } +} + +static void test_div_u32_by_10(void) +{ + for (unsigned i = 0; i < N_U32_VALS; i++) { + DEBUG("Dividing %"PRIu32" by 10...\n", u32_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u32_by_10(u32_test_values[i]), + u32_test_values[i]/10); + } +} + +static void test_div_u32_mod_10(void) +{ + for (unsigned i = 0; i < N_U32_VALS; i++) { + DEBUG("Calculating %"PRIu32" % 10...\n", u32_test_values[i]); + TEST_ASSERT_EQUAL_INT( + div_u32_mod_10(u32_test_values[i]), + u32_test_values[i]%10); + } +} + +Test *tests_div_tests(void) +{ + EMB_UNIT_TESTFIXTURES(fixtures) { + new_TestFixture(test_div_u64_by_15625), + new_TestFixture(test_div_u32_by_15625div512), + new_TestFixture(test_div_u64_by_1000000), + new_TestFixture(test_div_u32_by_10), + new_TestFixture(test_div_u32_mod_10), + }; + + EMB_UNIT_TESTCALLER(div_tests, NULL, NULL, fixtures); + + return (Test *)&div_tests; +} + +void tests_div(void) +{ + TESTS_RUN(tests_div_tests()); +} diff --git a/tests/unittests/tests-div/tests-div.h b/tests/unittests/tests-div/tests-div.h new file mode 100644 index 0000000000000000000000000000000000000000..037ee6b0210101e920185c1aec8191d2ef8f27d5 --- /dev/null +++ b/tests/unittests/tests-div/tests-div.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2015 Kaspar Schleiser <kaspar@schleiser.de> + * + * This file is subject to the terms and conditions of the GNU Lesser + * General Public License v2.1. See the file LICENSE in the top level + * directory for more details. + */ + +/** + * @addtogroup unittests + * @{ + * + * @file + * @brief Unittests for the ``div`` header + * + * @author Kaspar Schleiser <kaspar@schleiser.de> + */ +#ifndef TESTS_DIV_H_ +#define TESTS_DIV_H_ +#include "embUnit/embUnit.h" + +#ifdef __cplusplus +extern "C" { +#endif + +/** +* @brief The entry point of this test suite. +*/ +void tests_div(void); + +/** + * @brief Generates tests for div + * + * @return embUnit tests if successful, NULL if not. + */ +Test *tests_div_tests(void); + +#ifdef __cplusplus +} +#endif + +#endif /* TESTS_DIV_H_ */ +/** @} */