Initial Release
This commit is contained in:
395
src/rp2_common/hardware_divider/include/hardware/divider.h
Normal file
395
src/rp2_common/hardware_divider/include/hardware/divider.h
Normal file
@ -0,0 +1,395 @@
|
||||
/*
|
||||
* Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
|
||||
*
|
||||
* SPDX-License-Identifier: BSD-3-Clause
|
||||
*/
|
||||
|
||||
#ifndef _HARDWARE_DIVIDER_H
|
||||
#define _HARDWARE_DIVIDER_H
|
||||
|
||||
#include "pico.h"
|
||||
#include "hardware/structs/sio.h"
|
||||
|
||||
/** \file hardware/divider.h
|
||||
* \defgroup hardware_divider hardware_divider
|
||||
*
|
||||
* Low-level hardware-divider access
|
||||
*
|
||||
* The SIO contains an 8-cycle signed/unsigned divide/modulo circuit, per core. Calculation is started by writing a dividend
|
||||
* and divisor to the two argument registers, DIVIDEND and DIVISOR. The divider calculates the quotient / and remainder % of
|
||||
* this division over the next 8 cycles, and on the 9th cycle the results can be read from the two result registers
|
||||
* DIV_QUOTIENT and DIV_REMAINDER. A 'ready' bit in register DIV_CSR can be polled to wait for the calculation to
|
||||
* complete, or software can insert a fixed 8-cycle delay
|
||||
*
|
||||
* This header provides low level macros and inline functions for accessing the hardware dividers directly,
|
||||
* and perhaps most usefully performing asynchronous divides. These functions however do not follow the regular
|
||||
* Pico SDK conventions for saving/restoring the divider state, so are not generally safe to call from interrupt handlers
|
||||
*
|
||||
* The pico_divider library provides a more user friendly set of APIs over the divider (and support for
|
||||
* 64 bit divides), and of course by default regular C language integer divisions are redirected through that library, meaning
|
||||
* you can just use C level `/` and `%` operators and gain the benefits of the fast hardware divider.
|
||||
*
|
||||
* @see pico_divider
|
||||
*
|
||||
* \subsection divider_example Example
|
||||
* \addtogroup hardware_divider
|
||||
* \include hello_divider.c
|
||||
*/
|
||||
|
||||
typedef uint64_t divmod_result_t;
|
||||
|
||||
/*! \brief Start a signed asynchronous divide
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Start a divide of the specified signed parameters. You should wait for 8 cycles (__div_pause()) or wait for the ready bit to be set
|
||||
* (hw_divider_wait_ready()) prior to reading the results.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
*/
|
||||
static inline void hw_divider_divmod_s32_start(int32_t a, int32_t b) {
|
||||
check_hw_layout( sio_hw_t, div_sdividend, SIO_DIV_SDIVIDEND_OFFSET);
|
||||
sio_hw->div_sdividend = a;
|
||||
sio_hw->div_sdivisor = b;
|
||||
}
|
||||
|
||||
/*! \brief Start an unsigned asynchronous divide
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Start a divide of the specified unsigned parameters. You should wait for 8 cycles (__div_pause()) or wait for the ready bit to be set
|
||||
* (hw_divider_wait_ready()) prior to reading the results.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
*/
|
||||
static inline void hw_divider_divmod_u32_start(uint32_t a, uint32_t b) {
|
||||
check_hw_layout(
|
||||
sio_hw_t, div_udividend, SIO_DIV_UDIVIDEND_OFFSET);
|
||||
sio_hw->div_udividend = a;
|
||||
sio_hw->div_udivisor = b;
|
||||
}
|
||||
|
||||
/*! \brief Wait for a divide to complete
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Wait for a divide to complete
|
||||
*/
|
||||
static inline void hw_divider_wait_ready() {
|
||||
// this is #1 in lsr below
|
||||
static_assert(SIO_DIV_CSR_READY_BITS == 1, "");
|
||||
|
||||
// we use one less register and instruction than gcc which uses a TST instruction
|
||||
|
||||
uint32_t tmp; // allow compiler to pick scratch register
|
||||
asm volatile (
|
||||
"hw_divider_result_loop_%=:"
|
||||
"ldr %0, [%1, %2]\n\t"
|
||||
"lsr %0, #1\n\t"
|
||||
"bcc hw_divider_result_loop_%=\n\t"
|
||||
: "=&l" (tmp)
|
||||
: "l" (sio_hw), "I" (SIO_DIV_CSR_OFFSET)
|
||||
:
|
||||
);
|
||||
}
|
||||
|
||||
/*! \brief Return result of HW divide, nowait
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* \note This is UNSAFE in that the calculation may not have been completed.
|
||||
*
|
||||
* \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
|
||||
*/
|
||||
static inline divmod_result_t hw_divider_result_nowait() {
|
||||
// as ugly as this looks it is actually quite efficient
|
||||
divmod_result_t rc = (((divmod_result_t) sio_hw->div_remainder) << 32u) | sio_hw->div_quotient;
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Return result of last asynchronous HW divide
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* This function waits for the result to be ready by calling hw_divider_wait_ready().
|
||||
*
|
||||
* \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
|
||||
*/
|
||||
static inline divmod_result_t hw_divider_result_wait() {
|
||||
hw_divider_wait_ready();
|
||||
return hw_divider_result_nowait();
|
||||
}
|
||||
|
||||
/*! \brief Return result of last asynchronous HW divide, unsigned quotient only
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* This function waits for the result to be ready by calling hw_divider_wait_ready().
|
||||
*
|
||||
* \return Current unsigned quotient result.
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_quotient_wait() {
|
||||
hw_divider_wait_ready();
|
||||
return sio_hw->div_quotient;
|
||||
}
|
||||
|
||||
/*! \brief Return result of last asynchronous HW divide, signed quotient only
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* This function waits for the result to be ready by calling hw_divider_wait_ready().
|
||||
*
|
||||
* \return Current signed quotient result.
|
||||
*/
|
||||
static inline int32_t hw_divider_s32_quotient_wait() {
|
||||
hw_divider_wait_ready();
|
||||
return sio_hw->div_quotient;
|
||||
}
|
||||
|
||||
/*! \brief Return result of last asynchronous HW divide, unsigned remainder only
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* This function waits for the result to be ready by calling hw_divider_wait_ready().
|
||||
*
|
||||
* \return Current unsigned remainder result.
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_remainder_wait() {
|
||||
hw_divider_wait_ready();
|
||||
int32_t rc = sio_hw->div_remainder;
|
||||
sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Return result of last asynchronous HW divide, signed remainder only
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* This function waits for the result to be ready by calling hw_divider_wait_ready().
|
||||
*
|
||||
* \return Current remainder results.
|
||||
*/
|
||||
static inline int32_t hw_divider_s32_remainder_wait() {
|
||||
hw_divider_wait_ready();
|
||||
int32_t rc = sio_hw->div_remainder;
|
||||
sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Do a signed HW divide and wait for result
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return result as a fixed point 32p32 value.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Results of divide as a 32p32 fixed point value.
|
||||
*/
|
||||
divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b);
|
||||
|
||||
/*! \brief Do an unsigned HW divide and wait for result
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return result as a fixed point 32p32 value.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Results of divide as a 32p32 fixed point value.
|
||||
*/
|
||||
divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b);
|
||||
|
||||
/*! \brief Efficient extraction of unsigned quotient from 32p32 fixed point
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* \param r 32p32 fixed point value.
|
||||
* \return Unsigned quotient
|
||||
*/
|
||||
inline static uint32_t to_quotient_u32(divmod_result_t r) {
|
||||
return (uint32_t) r;
|
||||
}
|
||||
|
||||
/*! \brief Efficient extraction of signed quotient from 32p32 fixed point
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* \param r 32p32 fixed point value.
|
||||
* \return Unsigned quotient
|
||||
*/
|
||||
inline static int32_t to_quotient_s32(divmod_result_t r) {
|
||||
return (int32_t)(uint32_t)r;
|
||||
}
|
||||
|
||||
/*! \brief Efficient extraction of unsigned remainder from 32p32 fixed point
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* \param r 32p32 fixed point value.
|
||||
* \return Unsigned remainder
|
||||
*
|
||||
* \note On Arm this is just a 32 bit register move or a nop
|
||||
*/
|
||||
inline static uint32_t to_remainder_u32(divmod_result_t r) {
|
||||
return (uint32_t)(r >> 32u);
|
||||
}
|
||||
|
||||
/*! \brief Efficient extraction of signed remainder from 32p32 fixed point
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* \param r 32p32 fixed point value.
|
||||
* \return Signed remainder
|
||||
*
|
||||
* \note On arm this is just a 32 bit register move or a nop
|
||||
*/
|
||||
inline static int32_t to_remainder_s32(divmod_result_t r) {
|
||||
return (int32_t)(r >> 32u);
|
||||
}
|
||||
|
||||
/*! \brief Do an unsigned HW divide, wait for result, return quotient
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return quotient.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Quotient results of the divide
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b) {
|
||||
return to_quotient_u32(hw_divider_divmod_u32(a, b));
|
||||
}
|
||||
|
||||
/*! \brief Do an unsigned HW divide, wait for result, return remainder
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return remainder.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Remainder results of the divide
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b) {
|
||||
return to_remainder_u32(hw_divider_divmod_u32(a, b));
|
||||
}
|
||||
|
||||
/*! \brief Do a signed HW divide, wait for result, return quotient
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return quotient.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Quotient results of the divide
|
||||
*/
|
||||
static inline int32_t hw_divider_quotient_s32(int32_t a, int32_t b) {
|
||||
return to_quotient_s32(hw_divider_divmod_s32(a, b));
|
||||
}
|
||||
|
||||
/*! \brief Do a signed HW divide, wait for result, return remainder
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return remainder.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Remainder results of the divide
|
||||
*/
|
||||
static inline int32_t hw_divider_remainder_s32(int32_t a, int32_t b) {
|
||||
return to_remainder_s32(hw_divider_divmod_s32(a, b));
|
||||
}
|
||||
|
||||
/*! \brief Pause for exact amount of time needed for a asynchronous divide to complete
|
||||
* \ingroup hardware_divider
|
||||
*/
|
||||
static inline void hw_divider_pause() {
|
||||
asm volatile (
|
||||
"b _1_%=\n"
|
||||
"_1_%=:\n"
|
||||
"b _2_%=\n"
|
||||
"_2_%=:\n"
|
||||
"b _3_%=\n"
|
||||
"_3_%=:\n"
|
||||
"b _4_%=\n"
|
||||
"_4_%=:\n"
|
||||
:: : );
|
||||
}
|
||||
|
||||
/*! \brief Do a hardware unsigned HW divide, wait for result, return quotient
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return quotient.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Quotient result of the divide
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b) {
|
||||
hw_divider_divmod_u32_start(a, b);
|
||||
hw_divider_pause();
|
||||
return sio_hw->div_quotient;
|
||||
}
|
||||
|
||||
/*! \brief Do a hardware unsigned HW divide, wait for result, return remainder
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return remainder.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Remainder result of the divide
|
||||
*/
|
||||
static inline uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b) {
|
||||
hw_divider_divmod_u32_start(a, b);
|
||||
hw_divider_pause();
|
||||
int32_t rc = sio_hw->div_remainder;
|
||||
sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*! \brief Do a hardware signed HW divide, wait for result, return quotient
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return quotient.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Quotient result of the divide
|
||||
*/
|
||||
static inline int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b) {
|
||||
hw_divider_divmod_s32_start(a, b);
|
||||
hw_divider_pause();
|
||||
return sio_hw->div_quotient;
|
||||
}
|
||||
|
||||
/*! \brief Do a hardware signed HW divide, wait for result, return remainder
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Divide \p a by \p b, wait for calculation to complete, return remainder.
|
||||
*
|
||||
* \param a The dividend
|
||||
* \param b The divisor
|
||||
* \return Remainder result of the divide
|
||||
*/
|
||||
static inline int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b) {
|
||||
hw_divider_divmod_s32_start(a, b);
|
||||
hw_divider_pause();
|
||||
int32_t rc = sio_hw->div_remainder;
|
||||
sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
|
||||
return rc;
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
uint32_t values[4];
|
||||
} hw_divider_state_t;
|
||||
|
||||
/*! \brief Save the calling cores hardware divider state
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Copy the current core's hardware divider state into the provided structure. This method
|
||||
* waits for the divider results to be stable, then copies them to memory.
|
||||
* They can be restored via hw_divider_restore_state()
|
||||
*
|
||||
* \param dest the location to store the divider state
|
||||
*/
|
||||
void hw_divider_save_state(hw_divider_state_t *dest);
|
||||
|
||||
/*! \brief Load a saved hardware divider state into the current core's hardware divider
|
||||
* \ingroup hardware_divider
|
||||
*
|
||||
* Copy the passed hardware divider state into the hardware divider.
|
||||
*
|
||||
* \param src the location to load the divider state from
|
||||
*/
|
||||
|
||||
void hw_divider_restore_state(hw_divider_state_t *src);
|
||||
|
||||
#endif // _HARDWARE_DIVIDER_H
|
Reference in New Issue
Block a user