v2.0.0
divider.h
Go to the documentation of this file.
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
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
49extern "C" {
50#endif
51
52typedef uint64_t divmod_result_t;
53
54#if PICO_EMULATE_DIVIDER
55extern divmod_result_t hw_divider_results[NUM_CORES];
56
57static inline int __sign_of(int32_t v) {
58 return v > 0 ? 1 : (v < 0 ? -1 : 0);
59}
60#endif
61
71#if !PICO_EMULATE_DIVIDER
72divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b);
73#else
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
89#if !PICO_EMULATE_DIVIDER
90divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b);
91#else
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
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
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
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
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
187 static inline divmod_result_t hw_divider_result_wait(void) {
188 hw_divider_wait_ready();
189 return hw_divider_result_nowait();
190}
191
198 inline static uint32_t to_quotient_u32(divmod_result_t r) {
199 return (uint32_t) r;
200}
201
208 inline static int32_t to_quotient_s32(divmod_result_t r) {
209 return (int32_t)(uint32_t)r;
210}
211
220 inline static uint32_t to_remainder_u32(divmod_result_t r) {
221 return (uint32_t)(r >> 32u);
222}
223
232 inline static int32_t to_remainder_s32(divmod_result_t r) {
233 return (int32_t)(r >> 32u);
234}
235
236
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
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
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
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
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
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
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
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
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
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
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
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
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
472typedef struct {
473 uint32_t values[4];
474} hw_divider_state_t;
475#else
476typedef uint64_t hw_divider_state_t;
477#endif
478
488#if !PICO_EMULATE_DIVIDER
489void hw_divider_save_state(hw_divider_state_t *dest);
490#else
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
503#if !PICO_EMULATE_DIVIDER
504void hw_divider_restore_state(hw_divider_state_t *src);
505#else
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
hw_divider_quotient_s32
static int32_t hw_divider_quotient_s32(int32_t a, int32_t b)
Do a signed HW divide, wait for result, return quotient.
Definition: divider.h:348
to_quotient_u32
static uint32_t to_quotient_u32(divmod_result_t r)
Efficient extraction of unsigned quotient from 32p32 fixed point.
Definition: divider.h:198
hw_divider_s32_quotient_inlined
static int32_t hw_divider_s32_quotient_inlined(int32_t a, int32_t b)
Do a hardware signed HW divide, wait for result, return quotient.
Definition: divider.h:440
hw_divider_u32_remainder_wait
static uint32_t hw_divider_u32_remainder_wait(void)
Return result of last asynchronous HW divide, unsigned remainder only.
Definition: divider.h:276
hw_divider_divmod_u32_start
static void hw_divider_divmod_u32_start(uint32_t a, uint32_t b)
Start an unsigned asynchronous divide.
Definition: divider.h:126
hw_divider_divmod_s32_start
static void hw_divider_divmod_s32_start(int32_t a, int32_t b)
Start a signed asynchronous divide.
Definition: divider.h:107
hw_divider_remainder_s32
static int32_t hw_divider_remainder_s32(int32_t a, int32_t b)
Do a signed HW divide, wait for result, return remainder.
Definition: divider.h:365
hw_divider_u32_remainder_inlined
static uint32_t hw_divider_u32_remainder_inlined(uint32_t a, uint32_t b)
Do a hardware unsigned HW divide, wait for result, return remainder.
Definition: divider.h:419
hw_divider_u32_quotient_wait
static uint32_t hw_divider_u32_quotient_wait(void)
Return result of last asynchronous HW divide, unsigned quotient only.
Definition: divider.h:244
hw_divider_result_nowait
static divmod_result_t hw_divider_result_nowait(void)
Return result of HW divide, nowait.
Definition: divider.h:169
to_quotient_s32
static int32_t to_quotient_s32(divmod_result_t r)
Efficient extraction of signed quotient from 32p32 fixed point.
Definition: divider.h:208
to_remainder_s32
static int32_t to_remainder_s32(divmod_result_t r)
Efficient extraction of signed remainder from 32p32 fixed point.
Definition: divider.h:232
hw_divider_divmod_s32
static divmod_result_t hw_divider_divmod_s32(int32_t a, int32_t b)
Do a signed HW divide and wait for result.
Definition: divider.h:74
hw_divider_wait_ready
static void hw_divider_wait_ready(void)
Wait for a divide to complete.
Definition: divider.h:142
hw_divider_s32_remainder_inlined
static int32_t hw_divider_s32_remainder_inlined(int32_t a, int32_t b)
Do a hardware signed HW divide, wait for result, return remainder.
Definition: divider.h:459
hw_divider_u32_quotient
static uint32_t hw_divider_u32_quotient(uint32_t a, uint32_t b)
Do an unsigned HW divide, wait for result, return quotient.
Definition: divider.h:314
hw_divider_restore_state
static void hw_divider_restore_state(hw_divider_state_t *src)
Load a saved hardware divider state into the current core's hardware divider.
Definition: divider.h:506
hw_divider_u32_remainder
static uint32_t hw_divider_u32_remainder(uint32_t a, uint32_t b)
Do an unsigned HW divide, wait for result, return remainder.
Definition: divider.h:331
hw_divider_s32_quotient_wait
static int32_t hw_divider_s32_quotient_wait(void)
Return result of last asynchronous HW divide, signed quotient only.
Definition: divider.h:260
hw_divider_save_state
static void hw_divider_save_state(hw_divider_state_t *dest)
Save the calling cores hardware divider state.
Definition: divider.h:491
hw_divider_result_wait
static divmod_result_t hw_divider_result_wait(void)
Return result of last asynchronous HW divide.
Definition: divider.h:187
hw_divider_divmod_u32
static divmod_result_t hw_divider_divmod_u32(uint32_t a, uint32_t b)
Do an unsigned HW divide and wait for result.
Definition: divider.h:92
hw_divider_pause
static void hw_divider_pause(void)
Pause for exact amount of time needed for a asynchronous divide to complete.
Definition: divider.h:376
hw_divider_u32_quotient_inlined
static uint32_t hw_divider_u32_quotient_inlined(uint32_t a, uint32_t b)
Do a hardware unsigned HW divide, wait for result, return quotient.
Definition: divider.h:400
hw_divider_s32_remainder_wait
static int32_t hw_divider_s32_remainder_wait(void)
Return result of last asynchronous HW divide, signed remainder only.
Definition: divider.h:294
to_remainder_u32
static uint32_t to_remainder_u32(divmod_result_t r)
Efficient extraction of unsigned remainder from 32p32 fixed point.
Definition: divider.h:220
get_core_num
static __force_inline uint get_core_num(void)
Get the current core number.
Definition: platform.h:141
pico.h
sio_hw_t
Definition: sio.h:28