diff --git a/core/include/bitarithm.h b/core/include/bitarithm.h index e76bfae173..1278f35435 100644 --- a/core/include/bitarithm.h +++ b/core/include/bitarithm.h @@ -164,6 +164,45 @@ static inline unsigned bitarithm_lsb(unsigned v) } #endif +/** + * @brief Used for iterating over the bits in @p state. + * Returns the index of a set bit in @p state, returns @p state with that bit cleared. + * + * @warning This is a low-level helper function, arguments are not checked. + * It is intended to iterate over a bit map until all bits are cleared. + * Whether it starts with the highest or lowest bit will depend on what is fastest + * on the given hardware. + * + * @warning @p state must not be zero. + * + * @param[in] state Bit Map with at least one bit set + * @param[out] index Index of the first set bit. Must be initialized with 0 before the + * first call to this function, must not be modified between subsequent + * calls. + * + * @return new state value - must be treated as opaque value + * + */ +static inline unsigned bitarithm_test_and_clear(unsigned state, uint8_t *index) +{ +#if defined(BITARITHM_HAS_CLZ) + *index = 8 * sizeof(state) - __builtin_clz(state) - 1; + return state & ~(1 << *index); +#elif defined(BITARITHM_LSB_LOOKUP) + /* Source: http://graphics.stanford.edu/~seander/bithacks.html#ZerosOnRightMultLookup */ + extern const uint8_t MultiplyDeBruijnBitPosition[32]; + uint32_t least_bit = state & -state; + *index = MultiplyDeBruijnBitPosition[(least_bit * 0x077CB531U) >> 27]; + return state & ~least_bit; +#else + while ((state & 1) == 0) { + *index += 1; + state >>= 1; + } + return state & ~1; +#endif +} + #ifdef __cplusplus } #endif diff --git a/cpu/arm7_common/include/arm_cpu.h b/cpu/arm7_common/include/arm_cpu.h index e5810d96f3..5e64e2fc12 100644 --- a/cpu/arm7_common/include/arm_cpu.h +++ b/cpu/arm7_common/include/arm_cpu.h @@ -34,6 +34,17 @@ void arm_reset(void); */ #define STACK_CANARY_WORD (0xEAFFFFFEu) +/** + * @brief Select fastest bitarithm_lsb implementation + * @{ + */ +#ifdef __ARM_FEATURE_CLZ +#define BITARITHM_LSB_BUILTIN +#define BITARITHM_HAS_CLZ +#else +#define BITARITHM_LSB_LOOKUP +#endif +/** @} */ #ifdef __cplusplus } diff --git a/cpu/cortexm_common/include/cpu_conf_common.h b/cpu/cortexm_common/include/cpu_conf_common.h index 896ed618b9..28e9643621 100644 --- a/cpu/cortexm_common/include/cpu_conf_common.h +++ b/cpu/cortexm_common/include/cpu_conf_common.h @@ -63,6 +63,7 @@ extern "C" { */ #ifdef __ARM_FEATURE_CLZ #define BITARITHM_LSB_BUILTIN +#define BITARITHM_HAS_CLZ #else #define BITARITHM_LSB_LOOKUP #endif diff --git a/tests/bitarithm_timings/main.c b/tests/bitarithm_timings/main.c index 3b4fc14d46..6cdec07bc4 100644 --- a/tests/bitarithm_timings/main.c +++ b/tests/bitarithm_timings/main.c @@ -40,6 +40,15 @@ #define TIMEOUT (TIMEOUT_S * US_PER_SEC) #define PER_ITERATION (4) +#if ARCH_32_BIT +#define TEST_AND_CLEAR_TEST_MASK_0 (0xA2101045UL) +#define TEST_AND_CLEAR_TEST_MASK_1 (0x22101044UL) +#else +#define TEST_AND_CLEAR_TEST_MASK_0 (0x9045UL) +#define TEST_AND_CLEAR_TEST_MASK_1 (0x1244UL) + +#endif + static atomic_bool done; static void callback(void *unused) @@ -84,6 +93,36 @@ static void run_test(const char *name, unsigned (*test)(unsigned)) printf("+ %s: %lu iterations per second\r\n", name, (4*PER_ITERATION) * count / TIMEOUT_S); } +static unsigned do_test_and_clear(unsigned state) +{ + uint8_t index = 0; + unsigned found = 0; + + while (state) { + state = bitarithm_test_and_clear(state, &index); + found |= 1 << index; + } + + return found; +} + +static void run_test_test_and_clear(void) +{ + unsigned long count = 0; + atomic_store(&done, false); + + xtimer_t xtimer = { .callback = callback }; + xtimer_set(&xtimer, TIMEOUT); + + do { + assert(do_test_and_clear(TEST_AND_CLEAR_TEST_MASK_0) == TEST_AND_CLEAR_TEST_MASK_0); + assert(do_test_and_clear(TEST_AND_CLEAR_TEST_MASK_1) == TEST_AND_CLEAR_TEST_MASK_1); + ++count; + } while (atomic_load(&done) == false); + + printf("+ %s: %lu iterations per second\r\n", "bitarithm_test_and_clear", 2 * count / TIMEOUT_S); +} + #define run_test(test) run_test(#test, test) int main(void) @@ -93,6 +132,7 @@ int main(void) run_test(bitarithm_msb); run_test(bitarithm_lsb); run_test(bitarithm_bits_set); + run_test_test_and_clear(); printf("Done.\r\n"); return 0; diff --git a/tests/bitarithm_timings/tests/01-run.py b/tests/bitarithm_timings/tests/01-run.py index e8bfc9aa26..6ec1d4f831 100755 --- a/tests/bitarithm_timings/tests/01-run.py +++ b/tests/bitarithm_timings/tests/01-run.py @@ -15,6 +15,7 @@ def testfunc(child): child.expect(r'\+ bitarithm_msb: \d+ iterations per second') child.expect(r'\+ bitarithm_lsb: \d+ iterations per second') child.expect(r'\+ bitarithm_bits_set: \d+ iterations per second') + child.expect(r'\+ bitarithm_test_and_clear: \d+ iterations per second') child.expect_exact("Done.")