1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _HARDWARE_DIVIDER_H
8 #define _HARDWARE_DIVIDER_H
9 
10 #include "pico.h"
11 
12 /** \file hardware/divider.h
13  *  \defgroup hardware_divider hardware_divider
14  *
15  * \brief RP2040 Low Low-level hardware-divider API. Non-RP2040 platforms provide software versions of all the functions
16  *
17  * The SIO contains an 8-cycle signed/unsigned divide/modulo circuit, per core. Calculation is started by writing a dividend
18  * and divisor to the two argument registers, DIVIDEND and DIVISOR. The divider calculates the quotient / and remainder % of
19  * this division over the next 8 cycles, and on the 9th cycle the results can be read from the two result registers
20  * DIV_QUOTIENT and DIV_REMAINDER. A 'ready' bit in register DIV_CSR can be polled to wait for the calculation to
21  * complete, or software can insert a fixed 8-cycle delay
22  *
23  * This header provides low level macros and inline functions for accessing the hardware dividers directly,
24  * and perhaps most usefully performing asynchronous divides. These functions however do not follow the regular
25  * SDK conventions for saving/restoring the divider state, so are not generally safe to call from interrupt handlers
26  *
27  * The pico_divider library provides a more user friendly set of APIs over the divider (and support for
28  * 64 bit divides), and of course by default regular C language integer divisions are redirected through that library, meaning
29  * you can just use C level `/` and `%` operators and gain the benefits of the fast hardware divider.
30  *
31  * \if rp2350_specific
32  * On RP2350 there is no hardware divider, and the functions are implemented in software
33  * \endif
34  *
35  * @see pico_divider
36  *
37  * \subsection divider_example Example
38  * \addtogroup hardware_divider
39  * \include hello_divider.c
40  */
41 
42 #if HAS_SIO_DIVIDER
43 #include "hardware/structs/sio.h"
44 #else
45 #define PICO_EMULATE_DIVIDER 1
46 #endif
47 
48 #ifdef __cplusplus
49 extern "C" {
50 #endif
51 
52 typedef uint64_t divmod_result_t;
53 
54 #if PICO_EMULATE_DIVIDER
55 extern divmod_result_t hw_divider_results[NUM_CORES];
56 
__sign_of(int32_t v)57 static inline int __sign_of(int32_t v) {
58     return v > 0 ? 1 : (v < 0 ? -1 : 0);
59 }
60 #endif
61 
62 /*! \brief Do a signed HW divide and wait for result
63  *  \ingroup hardware_divider
64  *
65  * Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
66  *
67  * \param a The dividend
68  * \param b The divisor
69  * \return Results of divide as a pair of 32-bit quotient/remainder values.
70  */
71 #if !PICO_EMULATE_DIVIDER
72 divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b);
73 #else
hw_divider_divmod_s32(int32_t a,int32_t b)74 static inline divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b) {
75     if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-__sign_of(a));
76     return (((uint64_t)(a%b))<<32u) | (uint32_t)(a/b);
77 }
78 #endif
79 
80 /*! \brief Do an unsigned HW divide and wait for result
81  *  \ingroup hardware_divider
82  *
83  * Divide \p a by \p b, wait for calculation to complete, return result as a pair of 32-bit quotient/remainder values.
84  *
85  * \param a The dividend
86  * \param b The divisor
87  * \return Results of divide as a pair of 32-bit quotient/remainder values.
88  */
89 #if !PICO_EMULATE_DIVIDER
90 divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b);
91 #else
hw_divider_divmod_u32(uint32_t a,uint32_t b)92 static inline divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b)  {
93     if (!b) return (((uint64_t)a)<<32u) | (uint32_t)(-1); // todo check this
94     return (((uint64_t)(a%b))<<32u) | (a/b);
95 }
96 #endif
97 
98 /*! \brief Start a signed asynchronous divide
99  *  \ingroup hardware_divider
100  *
101  * 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
102  * (hw_divider_wait_ready()) prior to reading the results.
103  *
104  * \param a The dividend
105  * \param b The divisor
106  */
hw_divider_divmod_s32_start(int32_t a,int32_t b)107 static inline void hw_divider_divmod_s32_start(int32_t a, int32_t b) {
108 #if !PICO_EMULATE_DIVIDER
109     check_hw_layout( sio_hw_t, div_sdividend, SIO_DIV_SDIVIDEND_OFFSET);
110     sio_hw->div_sdividend = (uint32_t)a;
111     sio_hw->div_sdivisor = (uint32_t)b;
112 #else
113     hw_divider_divmod_s32(a, b);
114 #endif
115 }
116 
117 /*! \brief Start an unsigned asynchronous divide
118  *  \ingroup hardware_divider
119  *
120  * 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
121  * (hw_divider_wait_ready()) prior to reading the results.
122  *
123  * \param a The dividend
124  * \param b The divisor
125  */
hw_divider_divmod_u32_start(uint32_t a,uint32_t b)126 static inline void hw_divider_divmod_u32_start(uint32_t a, uint32_t b) {
127 #if !PICO_EMULATE_DIVIDER
128     check_hw_layout(
129             sio_hw_t, div_udividend, SIO_DIV_UDIVIDEND_OFFSET);
130     sio_hw->div_udividend = a;
131     sio_hw->div_udivisor = b;
132 #else
133     hw_divider_divmod_u32(a, b);
134 #endif
135 }
136 
137 /*! \brief Wait for a divide to complete
138  *  \ingroup hardware_divider
139  *
140  * Wait for a divide to complete
141  */
hw_divider_wait_ready(void)142 static inline void hw_divider_wait_ready(void) {
143 #if !PICO_EMULATE_DIVIDER
144     // this is #1 in lsr below
145     static_assert(SIO_DIV_CSR_READY_BITS == 1, "");
146 
147     // we use one less register and instruction than gcc which uses a TST instruction
148 
149     uint32_t tmp; // allow compiler to pick scratch register
150     pico_default_asm_volatile (
151     "hw_divider_result_loop_%=:"
152     "ldr %0, [%1, %2]\n\t"
153     "lsrs %0, %0, #1\n\t"
154     "bcc hw_divider_result_loop_%=\n\t"
155     : "=&l" (tmp)
156     : "l" (sio_hw), "I" (SIO_DIV_CSR_OFFSET)
157     : "cc"
158     );
159 #endif
160 }
161 
162 /*! \brief Return result of HW divide, nowait
163  *  \ingroup hardware_divider
164  *
165  * \note This is UNSAFE in that the calculation may not have been completed.
166  *
167  * \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
168  */
hw_divider_result_nowait(void)169 static inline divmod_result_t hw_divider_result_nowait(void) {
170 #if !PICO_EMULATE_DIVIDER
171     // as ugly as this looks it is actually quite efficient
172     divmod_result_t rc = ((divmod_result_t) sio_hw->div_remainder) << 32u;
173     rc |= sio_hw->div_quotient;
174     return rc;
175 #else
176     return hw_divider_results[get_core_num()];
177 #endif
178 }
179 
180 /*! \brief Return result of last asynchronous HW divide
181  *  \ingroup hardware_divider
182  *
183  * This function waits for the result to be ready by calling hw_divider_wait_ready().
184  *
185  * \return Current result. Most significant 32 bits are the remainder, lower 32 bits are the quotient.
186  */
hw_divider_result_wait(void)187 static inline divmod_result_t hw_divider_result_wait(void) {
188     hw_divider_wait_ready();
189     return hw_divider_result_nowait();
190 }
191 
192 /*! \brief Efficient extraction of unsigned quotient from 32p32 fixed point
193  *  \ingroup hardware_divider
194  *
195  * \param r A pair of 32-bit quotient/remainder values.
196  * \return Unsigned quotient
197  */
to_quotient_u32(divmod_result_t r)198 inline static uint32_t to_quotient_u32(divmod_result_t r) {
199     return (uint32_t) r;
200 }
201 
202 /*! \brief Efficient extraction of signed quotient from 32p32 fixed point
203  *  \ingroup hardware_divider
204  *
205  * \param r A pair of 32-bit quotient/remainder values.
206  * \return Unsigned quotient
207  */
to_quotient_s32(divmod_result_t r)208 inline static int32_t to_quotient_s32(divmod_result_t r) {
209     return (int32_t)(uint32_t)r;
210 }
211 
212 /*! \brief Efficient extraction of unsigned remainder from 32p32 fixed point
213  *  \ingroup hardware_divider
214  *
215  * \param r A pair of 32-bit quotient/remainder values.
216  * \return Unsigned remainder
217  *
218  * \note On Arm this is just a 32 bit register move or a nop
219  */
to_remainder_u32(divmod_result_t r)220 inline static uint32_t to_remainder_u32(divmod_result_t r) {
221     return (uint32_t)(r >> 32u);
222 }
223 
224 /*! \brief Efficient extraction of signed remainder from 32p32 fixed point
225  *  \ingroup hardware_divider
226  *
227  * \param r A pair of 32-bit quotient/remainder values.
228  * \return Signed remainder
229  *
230  * \note On arm this is just a 32 bit register move or a nop
231  */
to_remainder_s32(divmod_result_t r)232 inline static int32_t to_remainder_s32(divmod_result_t r) {
233     return (int32_t)(r >> 32u);
234 }
235 
236 
237 /*! \brief Return result of last asynchronous HW divide, unsigned quotient only
238  *  \ingroup hardware_divider
239  *
240  * This function waits for the result to be ready by calling hw_divider_wait_ready().
241  *
242  * \return Current unsigned quotient result.
243  */
hw_divider_u32_quotient_wait(void)244 static inline uint32_t hw_divider_u32_quotient_wait(void) {
245 #if !PICO_EMULATE_DIVIDER
246     hw_divider_wait_ready();
247     return sio_hw->div_quotient;
248 #else
249     return to_quotient_u32(hw_divider_result_wait());
250 #endif
251 }
252 
253 /*! \brief Return result of last asynchronous HW divide, signed quotient only
254  *  \ingroup hardware_divider
255  *
256  * This function waits for the result to be ready by calling hw_divider_wait_ready().
257  *
258  * \return Current signed quotient result.
259  */
hw_divider_s32_quotient_wait(void)260 static inline int32_t hw_divider_s32_quotient_wait(void) {
261 #if !PICO_EMULATE_DIVIDER
262     hw_divider_wait_ready();
263     return (int32_t)sio_hw->div_quotient;
264 #else
265     return to_quotient_s32(hw_divider_result_wait());
266 #endif
267 }
268 
269 /*! \brief Return result of last asynchronous HW divide, unsigned remainder only
270  *  \ingroup hardware_divider
271  *
272  * This function waits for the result to be ready by calling hw_divider_wait_ready().
273  *
274  * \return Current unsigned remainder result.
275  */
hw_divider_u32_remainder_wait(void)276 static inline uint32_t hw_divider_u32_remainder_wait(void) {
277 #if !PICO_EMULATE_DIVIDER
278     hw_divider_wait_ready();
279     uint32_t rc = sio_hw->div_remainder;
280     sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
281     return rc;
282 #else
283     return to_remainder_u32(hw_divider_result_wait());
284 #endif
285 }
286 
287 /*! \brief Return result of last asynchronous HW divide, signed remainder only
288  *  \ingroup hardware_divider
289  *
290  * This function waits for the result to be ready by calling hw_divider_wait_ready().
291  *
292  * \return Current remainder results.
293  */
hw_divider_s32_remainder_wait(void)294 static inline int32_t hw_divider_s32_remainder_wait(void) {
295 #if !PICO_EMULATE_DIVIDER
296     hw_divider_wait_ready();
297     int32_t rc = (int32_t)sio_hw->div_remainder;
298     sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
299     return rc;
300 #else
301     return to_remainder_s32(hw_divider_result_wait());
302 #endif
303 }
304 
305 /*! \brief Do an unsigned HW divide, wait for result, return quotient
306  *  \ingroup hardware_divider
307  *
308  * Divide \p a by \p b, wait for calculation to complete, return quotient.
309  *
310  * \param a The dividend
311  * \param b The divisor
312  * \return Quotient results of the divide
313  */
hw_divider_u32_quotient(uint32_t a,uint32_t b)314 static inline uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b) {
315 #if !PICO_EMULATE_DIVIDER
316     return to_quotient_u32(hw_divider_divmod_u32(a, b));
317 #else
318     return b ? (a / b) : (uint32_t)(-1);
319 #endif
320 }
321 
322 /*! \brief Do an unsigned HW divide, wait for result, return remainder
323  *  \ingroup hardware_divider
324  *
325  * Divide \p a by \p b, wait for calculation to complete, return remainder.
326  *
327  * \param a The dividend
328  * \param b The divisor
329  * \return Remainder results of the divide
330  */
hw_divider_u32_remainder(uint32_t a,uint32_t b)331 static inline uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b) {
332 #if !PICO_EMULATE_DIVIDER
333     return to_remainder_u32(hw_divider_divmod_u32(a, b));
334 #else
335     return b ? (a % b) : a;
336 #endif
337 }
338 
339 /*! \brief Do a signed HW divide, wait for result, return quotient
340  *  \ingroup hardware_divider
341  *
342  * Divide \p a by \p b, wait for calculation to complete, return quotient.
343  *
344  * \param a The dividend
345  * \param b The divisor
346  * \return Quotient results of the divide
347  */
hw_divider_quotient_s32(int32_t a,int32_t b)348 static inline int32_t hw_divider_quotient_s32(int32_t a, int32_t b) {
349 #if !PICO_EMULATE_DIVIDER
350     return to_quotient_s32(hw_divider_divmod_s32(a, b));
351 #else
352     return b ? (a / b) : -1;
353 #endif
354 }
355 
356 /*! \brief Do a signed HW divide, wait for result, return remainder
357  *  \ingroup hardware_divider
358  *
359  * Divide \p a by \p b, wait for calculation to complete, return remainder.
360  *
361  * \param a The dividend
362  * \param b The divisor
363  * \return Remainder results of the divide
364  */
hw_divider_remainder_s32(int32_t a,int32_t b)365 static inline int32_t hw_divider_remainder_s32(int32_t a, int32_t b) {
366 #if !PICO_EMULATE_DIVIDER
367     return to_remainder_s32(hw_divider_divmod_s32(a, b));
368 #else
369     return b ? (a % b) : a;
370 #endif
371 }
372 
373 /*! \brief Pause for exact amount of time needed for a asynchronous divide to complete
374  *  \ingroup hardware_divider
375  */
hw_divider_pause(void)376 static inline void hw_divider_pause(void) {
377 #if !PICO_EMULATE_DIVIDER
378     pico_default_asm_volatile(
379     "b _1_%=\n"
380     "_1_%=:\n"
381     "b _2_%=\n"
382     "_2_%=:\n"
383     "b _3_%=\n"
384     "_3_%=:\n"
385     "b _4_%=\n"
386     "_4_%=:\n"
387     :::);
388 #endif
389 }
390 
391 /*! \brief Do a hardware unsigned HW divide, wait for result, return quotient
392  *  \ingroup hardware_divider
393  *
394  * Divide \p a by \p b, wait for calculation to complete, return quotient.
395  *
396  * \param a The dividend
397  * \param b The divisor
398  * \return Quotient result of the divide
399  */
hw_divider_u32_quotient_inlined(uint32_t a,uint32_t b)400 static inline uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b) {
401 #if !PICO_EMULATE_DIVIDER
402     hw_divider_divmod_u32_start(a, b);
403     hw_divider_pause();
404     return sio_hw->div_quotient;
405 #else
406     return hw_divider_u32_quotient(a,b);
407 #endif
408 }
409 
410 /*! \brief Do a hardware unsigned HW divide, wait for result, return remainder
411  *  \ingroup hardware_divider
412  *
413  * Divide \p a by \p b, wait for calculation to complete, return remainder.
414  *
415  * \param a The dividend
416  * \param b The divisor
417  * \return Remainder result of the divide
418  */
hw_divider_u32_remainder_inlined(uint32_t a,uint32_t b)419 static inline uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b) {
420 #if !PICO_EMULATE_DIVIDER
421     hw_divider_divmod_u32_start(a, b);
422     hw_divider_pause();
423     uint32_t rc = sio_hw->div_remainder;
424     sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
425     return rc;
426 #else
427     return hw_divider_u32_remainder(a,b);
428 #endif
429 }
430 
431 /*! \brief Do a hardware signed HW divide, wait for result, return quotient
432  *  \ingroup hardware_divider
433  *
434  * Divide \p a by \p b, wait for calculation to complete, return quotient.
435  *
436  * \param a The dividend
437  * \param b The divisor
438  * \return Quotient result of the divide
439  */
hw_divider_s32_quotient_inlined(int32_t a,int32_t b)440 static inline int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b) {
441 #if !PICO_EMULATE_DIVIDER
442     hw_divider_divmod_s32_start(a, b);
443     hw_divider_pause();
444     return (int32_t)sio_hw->div_quotient;
445 #else
446     return hw_divider_quotient_s32(a,b);
447 #endif
448 }
449 
450 /*! \brief Do a hardware signed HW divide, wait for result, return remainder
451  *  \ingroup hardware_divider
452  *
453  * Divide \p a by \p b, wait for calculation to complete, return remainder.
454  *
455  * \param a The dividend
456  * \param b The divisor
457  * \return Remainder result of the divide
458  */
hw_divider_s32_remainder_inlined(int32_t a,int32_t b)459 static inline int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b) {
460 #if !PICO_EMULATE_DIVIDER
461     hw_divider_divmod_s32_start(a, b);
462     hw_divider_pause();
463     int32_t rc = (int32_t)sio_hw->div_remainder;
464     sio_hw->div_quotient; // must read quotient to cooperate with other SDK code
465     return rc;
466 #else
467     return hw_divider_remainder_s32(a,b);
468 #endif
469 }
470 
471 #if !PICO_EMULATE_DIVIDER
472 typedef struct {
473     uint32_t values[4];
474 } hw_divider_state_t;
475 #else
476 typedef uint64_t hw_divider_state_t;
477 #endif
478 
479 /*! \brief Save the calling cores hardware divider state
480  *  \ingroup hardware_divider
481  *
482  * Copy the current core's hardware divider state into the provided structure. This method
483  * waits for the divider results to be stable, then copies them to memory.
484  * They can be restored via hw_divider_restore_state()
485  *
486  * \param dest the location to store the divider state
487  */
488 #if !PICO_EMULATE_DIVIDER
489 void hw_divider_save_state(hw_divider_state_t *dest);
490 #else
hw_divider_save_state(hw_divider_state_t * dest)491 static inline void hw_divider_save_state(hw_divider_state_t *dest) {
492     *dest = hw_divider_results[get_core_num()];
493 }
494 #endif
495 
496 /*! \brief Load a saved hardware divider state into the current core's hardware divider
497  *  \ingroup hardware_divider
498  *
499  * Copy the passed hardware divider state into the hardware divider.
500  *
501  * \param src the location to load the divider state from
502  */
503 #if !PICO_EMULATE_DIVIDER
504 void hw_divider_restore_state(hw_divider_state_t *src);
505 #else
hw_divider_restore_state(hw_divider_state_t * src)506 static inline void hw_divider_restore_state(hw_divider_state_t *src) {
507     hw_divider_results[get_core_num()] = *src;
508 }
509 #endif
510 
511 #ifdef __cplusplus
512 }
513 #endif
514 
515 #endif // _HARDWARE_DIVIDER_H
516