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