1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_MULTICORE_H
8 #define _PICO_MULTICORE_H
9 
10 #include "pico/types.h"
11 #include "pico/sync.h"
12 #include "hardware/structs/sio.h"
13 
14 #ifdef __cplusplus
15 extern "C" {
16 #endif
17 
18 // PICO_CONFIG: PARAM_ASSERTIONS_ENABLED_PICO_MULTICORE, Enable/disable assertions in the pico_multicore module, type=bool, default=0, group=pico_multicore
19 #ifndef PARAM_ASSERTIONS_ENABLED_PICO_MULTICORE
20 #define PARAM_ASSERTIONS_ENABLED_PICO_MULTICORE 0
21 #endif
22 
23 /** \file multicore.h
24  * \defgroup pico_multicore pico_multicore
25  * \brief Adds support for running code on, and interacting with the second processor core (core 1).
26  *
27  * \subsection multicore_example Example
28  * \addtogroup pico_multicore
29  * \include multicore.c
30 */
31 
32 // PICO_CONFIG: PICO_CORE1_STACK_SIZE, Minimum amount of stack space reserved in the linker script for core 1 - if zero then no space is reserved and the user must provide their own stack, min=0, max=0x10000, default=PICO_STACK_SIZE (0x800), group=pico_multicore
33 #ifndef PICO_CORE1_STACK_SIZE
34 #ifdef PICO_STACK_SIZE
35 #define PICO_CORE1_STACK_SIZE PICO_STACK_SIZE
36 #else
37 #define PICO_CORE1_STACK_SIZE 0x800
38 #endif
39 #endif
40 
41 /**
42  * \def SIO_FIFO_IRQ_NUM(core)
43  * \ingroup pico_multicore
44  * \hideinitializer
45  * \brief Returns the \ref irq_num_t for the FIFO IRQ on the given core.
46  *
47  * \if rp2040_specific
48  * On RP2040 each core has a different IRQ number: `SIO_IRQ_PROC0` and `SIO_IRQ_PROC1`.
49  * \endif
50  * \if rp2350_specific
51  * On RP2350 both cores share the same irq number (`SIO_IRQ_PROC`) just with a different SIO
52  * interrupt output routed to that IRQ input on each core.
53  * \endif
54  *
55  * Note this macro is intended to resolve at compile time, and does no parameter checking
56  */
57 #ifndef SIO_FIFO_IRQ_NUM
58 #if !PICO_RP2040
59 #define SIO_FIFO_IRQ_NUM(core) SIO_IRQ_FIFO
60 #else
61 static_assert(SIO_IRQ_PROC1 == SIO_IRQ_PROC0 + 1, "");
62 #define SIO_FIFO_IRQ_NUM(core) (SIO_IRQ_PROC0 + (core))
63 #endif
64 #endif
65 
66 /*! \brief  Reset core 1
67  *  \ingroup pico_multicore
68  *
69  * This function can be used to reset core 1 into its initial state (ready for launching code against via \ref multicore_launch_core1 and similar methods)
70  *
71  * \note this function should only be called from core 0
72  */
73 void multicore_reset_core1(void);
74 
75 /*! \brief  Run code on core 1
76  *  \ingroup pico_multicore
77  *
78  * Wake up (a previously reset) core 1 and enter the given function on core 1 using the default core 1 stack (below core 0 stack).
79  *
80  * core 1 must previously have been reset either as a result of a system reset or by calling \ref multicore_reset_core1
81  *
82  * core 1 will use the same vector table as core 0
83  *
84  * \param entry Function entry point
85  * \see multicore_reset_core1
86  */
87 void multicore_launch_core1(void (*entry)(void));
88 
89 /*! \brief  Launch code on core 1 with stack
90  *  \ingroup pico_multicore
91  *
92  * Wake up (a previously reset) core 1 and enter the given function on core 1 using the passed stack for core 1
93  *
94  * core 1 must previously have been reset either as a result of a system reset or by calling \ref multicore_reset_core1
95  *
96  * core 1 will use the same vector table as core 0
97  *
98  * \param entry Function entry point
99  * \param stack_bottom The bottom (lowest address) of the stack
100  * \param stack_size_bytes The size of the stack in bytes (must be a multiple of 4)
101  * \see multicore_reset_core1
102  */
103 void multicore_launch_core1_with_stack(void (*entry)(void), uint32_t *stack_bottom, size_t stack_size_bytes);
104 
105 /*! \brief  Launch code on core 1 with no stack protection
106  *  \ingroup pico_multicore
107  *
108  * Wake up (a previously reset) core 1 and start it executing with a specific entry point, stack pointer
109  * and vector table.
110  *
111  * This is a low level function that does not provide a stack guard even if USE_STACK_GUARDS is defined
112  *
113  * core 1 must previously have been reset either as a result of a system reset or by calling \ref multicore_reset_core1
114  *
115  * \param entry Function entry point
116  * \param sp Pointer to the top of the core 1 stack
117  * \param vector_table address of the vector table to use for core 1
118  * \see multicore_reset_core1
119  */
120 void multicore_launch_core1_raw(void (*entry)(void), uint32_t *sp, uint32_t vector_table);
121 
122 /*!
123  * \defgroup multicore_fifo fifo
124  * \ingroup pico_multicore
125  * \brief Functions for the inter-core FIFOs
126  *
127  * RP-series microcontrollers contains two FIFOs for passing data, messages or ordered events between the two cores. Each FIFO
128  * is 32 bits wide, and 8 entries deep on the RP2040, and 4 entries deep on the RP2350. One of the FIFOs can only be written by
129  * core 0, and read by core 1. The other can only be written by core 1, and read by core 0.
130  *
131  * \note The inter-core FIFOs are a very precious resource and are frequently used for SDK functionality (e.g. during
132  * core 1 launch or by the \ref multicore_lockout functions). Additionally they are often required for the exclusive use
133  * of an RTOS (e.g. FreeRTOS SMP). For these reasons it is suggested that you do not use the FIFO for your own purposes
134  * unless none of the above concerns apply; the majority of cases for transferring data between cores can be eqaully
135  * well handled by using a \ref queue
136  */
137 
138 /*! \brief Check the read FIFO to see if there is data available (sent by the other core)
139  *  \ingroup multicore_fifo
140  *
141  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
142  *
143  * \return true if the FIFO has data in it, false otherwise
144  */
multicore_fifo_rvalid(void)145 static inline bool multicore_fifo_rvalid(void) {
146     return sio_hw->fifo_st & SIO_FIFO_ST_VLD_BITS;
147 }
148 
149 /*! \brief Check the write FIFO to see if it has space for more data
150  *  \ingroup multicore_fifo
151  *
152  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
153  *
154  *  @return true if the FIFO has room for more data, false otherwise
155  */
multicore_fifo_wready(void)156 static inline bool multicore_fifo_wready(void) {
157     return sio_hw->fifo_st & SIO_FIFO_ST_RDY_BITS;
158 }
159 
160 /*! \brief Push data on to the write FIFO (data to the other core).
161  *  \ingroup multicore_fifo
162  *
163  * This function will block until there is space for the data to be sent.
164  * Use \ref multicore_fifo_wready() to check if it is possible to write to the
165  * FIFO if you don't want to block.
166  *
167  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
168  *
169  * \param data A 32 bit value to push on to the FIFO
170  */
171 void multicore_fifo_push_blocking(uint32_t data);
172 
173 /*! \brief Push data on to the write FIFO (data to the other core).
174  *  \ingroup multicore_fifo
175  *
176  * This function will block until there is space for the data to be sent.
177  * Use multicore_fifo_wready() to check if it is possible to write to the
178  * FIFO if you don't want to block.
179  *
180  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
181  *
182  * \param data A 32 bit value to push on to the FIFO
183  */
multicore_fifo_push_blocking_inline(uint32_t data)184 static inline void multicore_fifo_push_blocking_inline(uint32_t data) {
185     // We wait for the fifo to have some space
186     while (!multicore_fifo_wready())
187         tight_loop_contents();
188 
189     sio_hw->fifo_wr = data;
190 
191     // Fire off an event to the other core
192     __sev();
193 }
194 
195 /*! \brief Push data on to the write FIFO (data to the other core) with timeout.
196  *  \ingroup multicore_fifo
197  *
198  * This function will block until there is space for the data to be sent
199  * or the timeout is reached
200  *
201  * \param data A 32 bit value to push on to the FIFO
202  * \param timeout_us the timeout in microseconds
203  * \return true if the data was pushed, false if the timeout occurred before data could be pushed
204  */
205 bool multicore_fifo_push_timeout_us(uint32_t data, uint64_t timeout_us);
206 
207 /*! \brief Pop data from the read FIFO (data from the other core).
208  *  \ingroup multicore_fifo
209  *
210  * This function will block until there is data ready to be read
211  * Use multicore_fifo_rvalid() to check if data is ready to be read if you don't
212  * want to block.
213  *
214  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
215  *
216  * \return 32 bit data from the read FIFO.
217  */
218 uint32_t multicore_fifo_pop_blocking(void);
219 
220 /*! \brief Pop data from the read FIFO (data from the other core).
221  *  \ingroup multicore_fifo
222  *
223  * This function will block until there is data ready to be read
224  * Use multicore_fifo_rvalid() to check if data is ready to be read if you don't
225  * want to block.
226  *
227  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
228  *
229  * \return 32 bit data from the read FIFO.
230  */
multicore_fifo_pop_blocking_inline(void)231 static inline uint32_t multicore_fifo_pop_blocking_inline(void) {
232     // If nothing there yet, we wait for an event first,
233     // to try and avoid too much busy waiting
234     while (!multicore_fifo_rvalid())
235         __wfe();
236 
237     return sio_hw->fifo_rd;
238 }
239 
240 /*! \brief Pop data from the read FIFO (data from the other core) with timeout.
241  *  \ingroup multicore_fifo
242  *
243  * This function will block until there is data ready to be read or the timeout is reached
244  *
245  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
246  *
247  * \param timeout_us the timeout in microseconds
248  * \param out the location to store the popped data if available
249  * \return true if the data was popped and a value copied into `out`, false if the timeout occurred before data could be popped
250  */
251 bool multicore_fifo_pop_timeout_us(uint64_t timeout_us, uint32_t *out);
252 
253 /*! \brief Discard any data in the read FIFO
254  *  \ingroup multicore_fifo
255  *
256  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
257  */
multicore_fifo_drain(void)258 static inline void multicore_fifo_drain(void) {
259     while (multicore_fifo_rvalid())
260         (void) sio_hw->fifo_rd;
261 }
262 
263 /*! \brief Clear FIFO interrupt
264  *  \ingroup multicore_fifo
265  *
266  * Note that this only clears an interrupt that was caused by the ROE or WOF flags.
267  * To clear the VLD flag you need to use one of the 'pop' or 'drain' functions.
268  *
269  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
270  *
271  * \see multicore_fifo_get_status
272 */
multicore_fifo_clear_irq(void)273 static inline void multicore_fifo_clear_irq(void) {
274     // Write any value to clear the error flags
275     sio_hw->fifo_st = 0xff;
276 }
277 
278 /*! \brief Get FIFO statuses
279  *  \ingroup multicore_fifo
280  *
281  * \return The statuses as a bitfield
282  *
283  * Bit | Description
284  * ----|------------
285  * 3 | Sticky flag indicating the RX FIFO was read when empty (ROE). This read was ignored by the FIFO.
286  * 2 | Sticky flag indicating the TX FIFO was written when full (WOF). This write was ignored by the FIFO.
287  * 1 | Value is 1 if this core’s TX FIFO is not full (i.e. if FIFO_WR is ready for more data)
288  * 0 | Value is 1 if this core’s RX FIFO is not empty (i.e. if FIFO_RD is valid)
289  *
290  * See the note in the \ref multicore_fifo section for considerations regarding use of the inter-core FIFOs
291  *
292 */
multicore_fifo_get_status(void)293 static inline uint32_t multicore_fifo_get_status(void) {
294     return sio_hw->fifo_st;
295 }
296 
297 /*!
298  * \defgroup multicore_doorbell doorbell
299  * \ingroup pico_multicore
300  * \brief Functions related to doorbells which a core can use to raise IRQs on itself or the other core.
301  *
302  * \if (rp2040_specific && !combined_docs)
303  * The doorbell functionality is not available on RP2040.
304  * \endif
305  */
306 
307 #if NUM_DOORBELLS
check_doorbell_num_param(__unused uint doorbell_num)308 static inline void check_doorbell_num_param(__unused uint doorbell_num) {
309     invalid_params_if(PICO_MULTICORE, doorbell_num >= NUM_DOORBELLS);
310 }
311 
312 /*! \brief Cooperatively claim the use of this hardware alarm_num
313  *  \ingroup multicore_doorbell
314  *
315  * This method hard asserts if the hardware alarm is currently claimed.
316  *
317  * \param doorbell_num the doorbell number to claim
318  * \param core_mask 0b01: core 0, 0b10: core 1, 0b11 both core 0 and core 1
319  * \sa hardware_claiming
320  */
321 void multicore_doorbell_claim(uint doorbell_num, uint core_mask);
322 
323 /*! \brief Cooperatively claim the use of this hardware alarm_num
324  *  \ingroup multicore_doorbell
325  *
326  * This method attempts to claim an unused hardware alarm
327  *
328  * \param core_mask 0b01: core 0, 0b10: core 1, 0b11 both core 0 and core 1
329  * \param required if true the function will panic if none are available
330  * \return the doorbell number claimed or -1 if required was false, and none are available
331  * \sa hardware_claiming
332  */
333 int multicore_doorbell_claim_unused(uint core_mask, bool required);
334 
335 /*! \brief Cooperatively release the claim on use of this hardware alarm_num
336  *  \ingroup multicore_doorbell
337  *
338  * \param doorbell_num the doorbell number to unclaim
339  * \param core_mask 0b01: core 0, 0b10: core 1, 0b11 both core 0 and core 1
340  * \sa hardware_claiming
341  */
342 void multicore_doorbell_unclaim(uint doorbell_num, uint core_mask);
343 
344 /*! \brief Activate the given doorbell on the other core
345  * \ingroup multicore_doorbell
346  * \param doorbell_num the doorbell number
347  */
multicore_doorbell_set_other_core(uint doorbell_num)348 static inline void multicore_doorbell_set_other_core(uint doorbell_num) {
349     check_doorbell_num_param(doorbell_num);
350     sio_hw->doorbell_out_set = 1u << doorbell_num;
351 }
352 
353 /*! \brief Deactivate the given doorbell on the other core
354  * \ingroup multicore_doorbell
355  * \param doorbell_num the doorbell number
356  */
multicore_doorbell_clear_other_core(uint doorbell_num)357 static inline void multicore_doorbell_clear_other_core(uint doorbell_num) {
358     check_doorbell_num_param(doorbell_num);
359     sio_hw->doorbell_out_clr = 1u << doorbell_num;
360 }
361 
362 /*! \brief Activate the given doorbell on this core
363  * \ingroup multicore_doorbell
364  * \param doorbell_num the doorbell number
365  */
multicore_doorbell_set_current_core(uint doorbell_num)366 static inline void multicore_doorbell_set_current_core(uint doorbell_num) {
367     check_doorbell_num_param(doorbell_num);
368     sio_hw->doorbell_in_set = 1u << doorbell_num;
369 }
370 
371 /*! \brief Deactivate the given doorbell on this core
372  * \ingroup multicore_doorbell
373  * \param doorbell_num the doorbell number
374  */
multicore_doorbell_clear_current_core(uint doorbell_num)375 static inline void multicore_doorbell_clear_current_core(uint doorbell_num) {
376     check_doorbell_num_param(doorbell_num);
377     sio_hw->doorbell_in_clr = 1u << doorbell_num;
378 }
379 
380 /*! \brief Determine if the given doorbell is active on the other core
381  * \ingroup multicore_doorbell
382  * \param doorbell_num the doorbell number
383  */
multicore_doorbell_is_set_current_core(uint doorbell_num)384 static inline bool multicore_doorbell_is_set_current_core(uint doorbell_num) {
385     check_doorbell_num_param(doorbell_num);
386     return sio_hw->doorbell_in_set & (1u << doorbell_num);
387 }
388 
389 /*! \brief Determine if the given doorbell is active on the this core
390  * \ingroup multicore_doorbell
391  * \param doorbell_num the doorbell number
392  */
multicore_doorbell_is_set_other_core(uint doorbell_num)393 static inline bool multicore_doorbell_is_set_other_core(uint doorbell_num) {
394     check_doorbell_num_param(doorbell_num);
395     return sio_hw->doorbell_out_set & (1u << doorbell_num);
396 }
397 
398 /**
399  * \def DOORBELL_IRQ_NUM(doorbell_num)
400  * \ingroup multicore_doorbell
401  * \hideinitializer
402  * \brief Returns the \ref irq_num_t for processor interrupts for the given doorbell number
403  *
404  * Note this macro is intended to resolve at compile time, and does no parameter checking
405  */
406 #ifndef DOORBELL_IRQ_NUM
407 #define DOORBELL_IRQ_NUM(doorbell_num) SIO_IRQ_BELL
408 #endif
409 
multicore_doorbell_irq_num(uint doorbell_num)410 static inline uint multicore_doorbell_irq_num(uint doorbell_num) {
411     check_doorbell_num_param(doorbell_num);
412     return DOORBELL_IRQ_NUM(doorbell_num);
413 }
414 
415 #endif
416 
417 /*!
418  * \defgroup multicore_lockout lockout
419  * \ingroup pico_multicore
420  * \brief Functions to enable one core to force the other core to pause execution in a known state
421  *
422  * Sometimes it is useful to enter a critical section on both cores at once. On a single
423  * core system a critical section can trivially be entered by disabling interrupts, however on a multi-core
424  * system that is not sufficient, and unless the other core is polling in some way, then it will need to be interrupted
425  * in order to cooperatively enter a blocked state.
426  *
427  * These "lockout" functions use the inter core FIFOs to cause an interrupt on one core from the other, and manage
428  * waiting for the other core to enter the "locked out" state.
429  *
430  * The usage is that the "victim" core ... i.e the core that can be "locked out" by the other core calls
431  * \ref multicore_lockout_victim_init to hook the FIFO interrupt. Note that either or both cores may do this.
432  *
433  * \note When "locked out" the victim core is paused (it is actually executing a tight loop with code in RAM) and has interrupts disabled.
434  * This makes the lockout functions suitable for use by code that wants to write to flash (at which point no code may be executing
435  * from flash)
436  *
437  * The core which wishes to lockout the other core calls \ref multicore_lockout_start_blocking or
438  * \ref multicore_lockout_start_timeout_us to interrupt the other "victim" core and wait for it to be in a
439  * "locked out" state. Once the lockout is no longer needed it calls \ref multicore_lockout_end_blocking or
440  * \ref multicore_lockout_end_timeout_us to release the lockout and wait for confirmation.
441  *
442  * \note Because multicore lockout uses the intercore FIFOs, the FIFOs <b>cannot</b> be used for any other purpose
443  */
444 
445 /*! \brief Initialize the current core such that it can be a "victim" of lockout (i.e. forced to pause in a known state by the other core)
446  *  \ingroup multicore_lockout
447  *
448  * This code hooks the intercore FIFO IRQ, and the FIFO may not be used for any other purpose after this.
449  */
450 void multicore_lockout_victim_init(void);
451 
452 /*! \brief Determine if \ref multicore_lockout_victim_init() has been called on the specified core.
453  *  \ingroup multicore_lockout
454  *
455  * \note this state persists even if the core is subsequently reset; therefore you are advised to
456  * always call \ref multicore_lockout_victim_init() again after resetting a core, which had previously
457  * been initialized.
458  *
459  * \param core_num the core number (0 or 1)
460  * \return true if \ref multicore_lockout_victim_init() has been called on the specified core, false otherwise.
461  */
462 bool multicore_lockout_victim_is_initialized(uint core_num);
463 
464 /*! \brief Request the other core to pause in a known state and wait for it to do so
465  *  \ingroup multicore_lockout
466  *
467  * The other (victim) core must have previously executed \ref multicore_lockout_victim_init()
468  *
469  * \note multicore_lockout_start_ functions are not nestable, and must be paired with a call to a corresponding
470  * \ref multicore_lockout_end_blocking
471  */
472 void multicore_lockout_start_blocking(void);
473 
474 /*! \brief Request the other core to pause in a known state and wait up to a time limit for it to do so
475  *  \ingroup multicore_lockout
476  *
477  * The other core must have previously executed \ref multicore_lockout_victim_init()
478  *
479  * \note multicore_lockout_start_ functions are not nestable, and must be paired with a call to a corresponding
480  * \ref multicore_lockout_end_blocking
481  *
482  * \param timeout_us the timeout in microseconds
483  * \return true if the other core entered the locked out state within the timeout, false otherwise
484  */
485 bool multicore_lockout_start_timeout_us(uint64_t timeout_us);
486 
487 /*! \brief Release the other core from a locked out state amd wait for it to acknowledge
488  *  \ingroup multicore_lockout
489  *
490  * \note The other core must previously have been "locked out" by calling a `multicore_lockout_start_` function
491  * from this core
492  */
493 void multicore_lockout_end_blocking(void);
494 
495 /*! \brief Release the other core from a locked out state amd wait up to a time limit for it to acknowledge
496  *  \ingroup multicore_lockout
497  *
498  * The other core must previously have been "locked out" by calling a `multicore_lockout_start_` function
499  * from this core
500  *
501  * \note be very careful using small timeout values, as a timeout here will leave the "lockout" functionality
502  * in a bad state. It is probably preferable to use \ref multicore_lockout_end_blocking anyway as if you have
503  * already waited for the victim core to enter the lockout state, then the victim core will be ready to exit
504  * the lockout state very quickly.
505  *
506  * \param timeout_us the timeout in microseconds
507  * \return true if the other core successfully exited locked out state within the timeout, false otherwise
508  */
509 bool multicore_lockout_end_timeout_us(uint64_t timeout_us);
510 
511 #ifdef __cplusplus
512 }
513 #endif
514 #endif
515