1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 #ifndef _PICO_PLATFORM_H
8 #define _PICO_PLATFORM_H
9 
10 /** \file platform.h
11  *  \defgroup pico_platform pico_platform
12  *
13  * Macros and definitions (and functions when included by non assembly code) for the RP2 family device / architecture
14  * to provide a common abstraction over low level compiler / platform specifics.
15  *
16  * This header may be included by assembly code
17  */
18 
19 #include "hardware/platform_defs.h"
20 #include "hardware/regs/addressmap.h"
21 #include "hardware/regs/sio.h"
22 
23 // Marker for builds targeting the RP2040
24 #define PICO_RP2040 1
25 
26 // PICO_CONFIG: PICO_STACK_SIZE, Stack Size, min=0x100, default=0x800, advanced=true, group=pico_platform
27 #ifndef PICO_STACK_SIZE
28 #define PICO_STACK_SIZE _u(0x800)
29 #endif
30 
31 // PICO_CONFIG: PICO_HEAP_SIZE, Heap size to reserve, min=0x100, default=0x800, advanced=true, group=pico_platform
32 #ifndef PICO_HEAP_SIZE
33 #define PICO_HEAP_SIZE _u(0x800)
34 #endif
35 
36 // PICO_CONFIG: PICO_NO_RAM_VECTOR_TABLE, Enable/disable the RAM vector table, type=bool, default=0, advanced=true, group=pico_platform
37 #ifndef PICO_NO_RAM_VECTOR_TABLE
38 #define PICO_NO_RAM_VECTOR_TABLE 0
39 #endif
40 
41 // PICO_CONFIG: PICO_RP2040_B0_SUPPORTED, Whether to include any specific software support for RP2040 B0 revision, type=bool, default=1, advanced=true, group=pico_platform
42 #ifndef PICO_RP2040_B0_SUPPORTED
43 #define PICO_RP2040_B0_SUPPORTED 1
44 #endif
45 
46 // PICO_CONFIG: PICO_FLOAT_SUPPORT_ROM_V1, Include float support code for RP2040 B0 when that chip revision is supported , type=bool, default=1, advanced=true, group=pico_platform
47 #ifndef PICO_FLOAT_SUPPORT_ROM_V1
48 #define PICO_FLOAT_SUPPORT_ROM_V1 1
49 #endif
50 
51 // PICO_CONFIG: PICO_DOUBLE_SUPPORT_ROM_V1, Include double support code for RP2040 B0 when that chip revision is supported , type=bool, default=1, advanced=true, group=pico_platform
52 #ifndef PICO_DOUBLE_SUPPORT_ROM_V1
53 #define PICO_DOUBLE_SUPPORT_ROM_V1 1
54 #endif
55 
56 
57 // PICO_CONFIG: PICO_RP2040_B1_SUPPORTED, Whether to include any specific software support for RP2040 B1 revision, type=bool, default=1, advanced=true, group=pico_platform
58 #ifndef PICO_RP2040_B1_SUPPORTED
59 #define PICO_RP2040_B1_SUPPORTED 1
60 #endif
61 
62 // PICO_CONFIG: PICO_RP2040_B2_SUPPORTED, Whether to include any specific software support for RP2040 B2 revision, type=bool, default=1, advanced=true, group=pico_platform
63 #ifndef PICO_RP2040_B2_SUPPORTED
64 #define PICO_RP2040_B2_SUPPORTED 1
65 #endif
66 
67 // --- remainder of file is not included by assembly code ---
68 
69 #ifndef __ASSEMBLER__
70 
71 #if defined __GNUC__
72 #include <sys/cdefs.h>
73 // note LLVM defines __GNUC__
74 #ifdef __clang__
75 #define PICO_C_COMPILER_IS_CLANG 1
76 #else
77 #define PICO_C_COMPILER_IS_GNU 1
78 #endif
79 #elif defined __ICCARM__
80 #ifndef __aligned
81 #define __aligned(x)	__attribute__((__aligned__(x)))
82 #endif
83 #ifndef __always_inline
84 #define __always_inline __attribute__((__always_inline__))
85 #endif
86 #ifndef __noinline
87 #define __noinline      __attribute__((__noinline__))
88 #endif
89 #ifndef __packed
90 #define __packed        __attribute__((__packed__))
91 #endif
92 #ifndef __printflike
93 #define __printflike(a, b)
94 #endif
95 #ifndef __unused
96 #define __unused        __attribute__((__unused__))
97 #endif
98 #ifndef __used
99 #define __used          __attribute__((__used__))
100 #endif
101 #ifndef __CONCAT1
102 #define __CONCAT1(a, b) a ## b
103 #endif
104 #ifndef __CONCAT
105 #define __CONCAT(a, b)  __CONCAT1(a, b)
106 #endif
107 #ifndef __STRING
108 #define __STRING(a)     #a
109 #endif
110 /* Compatible definitions of GCC builtins */
111 
__builtin_ctz(uint x)112 static inline uint __builtin_ctz(uint x) {
113   extern uint32_t __ctzsi2(uint32_t);
114   return __ctzsi2(x);
115 }
116 #define __builtin_expect(x, y) (x)
117 #define __builtin_isnan(x) __iar_isnan(x)
118 #else
119 #error Unsupported toolchain
120 #endif
121 
122 #include "pico/types.h"
123 
124 // GCC_Like_Pragma(x) is a pragma on GNUC compatible compilers
125 #ifdef __GNUC__
126 #define GCC_Like_Pragma _Pragma
127 #else
128 #define GCC_Like_Pragma(x)
129 #endif
130 
131 // Clang_Pragma(x) is a pragma on Clang only
132 #ifdef __clang__
133 #define Clang_Pragma _Pragma
134 #else
135 #define Clang_Pragma(x)
136 #endif
137 
138 // GCC_Pragma(x) is a pragma on GCC only
139 #if PICO_C_COMPILER_IS_GNU
140 #define GCC_Pragma _Pragma
141 #else
142 #define GCC_Pragma(x)
143 #endif
144 
145 #ifdef __cplusplus
146 extern "C" {
147 #endif
148 
149 /*! \brief Marker for an interrupt handler
150  *  \ingroup pico_platform
151  *
152  * For example an IRQ handler function called my_interrupt_handler:
153  *
154  *     void __isr my_interrupt_handler(void) {
155  */
156 #define __isr
157 
158 /*! \brief Section attribute macro for placement in RAM after the `.data` section
159  *  \ingroup pico_platform
160  *
161  * For example a 400 element `uint32_t` array placed after the .data section
162  *
163  *     uint32_t __after_data("my_group_name") a_big_array[400];
164  *
165  * The section attribute is `.after_data.<group>`
166  *
167  * \param group a string suffix to use in the section name to distinguish groups that can be linker
168  *              garbage-collected independently
169  */
170 #define __after_data(group) __attribute__((section(".after_data." group)))
171 
172 /*! \brief Section attribute macro for placement not in flash (i.e in RAM)
173  *  \ingroup pico_platform
174  *
175  * For example a 3 element `uint32_t` array placed in RAM (even though it is `static const`)
176  *
177  *     static const uint32_t __not_in_flash("my_group_name") an_array[3];
178  *
179  * The section attribute is `.time_critical.<group>`
180  *
181  * \param group a string suffix to use in the section name to distinguish groups that can be linker
182  *              garbage-collected independently
183  */
184 #define __not_in_flash(group) __attribute__((section(".time_critical." group)))
185 
186 /*! \brief Section attribute macro for placement in the SRAM bank 4 (known as "scratch X")
187  *  \ingroup pico_platform
188  *
189  * Scratch X is commonly used for critical data and functions accessed only by one core (when only
190  * one core is accessing the RAM bank, there is no opportunity for stalls)
191  *
192  * For example a `uint32_t` variable placed in "scratch X"
193  *
194  *     uint32_t __scratch_x("my_group_name") foo = 23;
195  *
196  * The section attribute is `.scratch_x.<group>`
197  *
198  * \param group a string suffix to use in the section name to distinguish groups that can be linker
199  *              garbage-collected independently
200  */
201 #define __scratch_x(group) __attribute__((section(".scratch_x." group)))
202 
203 /*! \brief Section attribute macro for placement in the SRAM bank 5 (known as "scratch Y")
204  *  \ingroup pico_platform
205  *
206  * Scratch Y is commonly used for critical data and functions accessed only by one core (when only
207  * one core is accessing the RAM bank, there is no opportunity for stalls)
208  *
209  * For example a `uint32_t` variable placed in "scratch Y"
210  *
211  *     uint32_t __scratch_y("my_group_name") foo = 23;
212  *
213  * The section attribute is `.scratch_y.<group>`
214  *
215  * \param group a string suffix to use in the section name to distinguish groups that can be linker
216  *              garbage-collected independently
217  */
218 #define __scratch_y(group) __attribute__((section(".scratch_y." group)))
219 
220 /*! \brief Section attribute macro for data that is to be left uninitialized
221  *  \ingroup pico_platform
222  *
223  * Data marked this way will retain its value across a reset (normally uninitialized data - in the .bss
224  * section) is initialized to zero during runtime initialization
225  *
226  * For example a `uint32_t` foo that will retain its value if the program is restarted by reset.
227  *
228  *     uint32_t __uninitialized_ram(foo);
229  *
230  * The section attribute is `.uninitialized_data.<group>`
231  *
232  * \param group a string suffix to use in the section name to distinguish groups that can be linker
233  *              garbage-collected independently
234  */
235 #define __uninitialized_ram(group) __attribute__((section(".uninitialized_data." #group))) group
236 
237 /*! \brief Section attribute macro for placement in flash even in a COPY_TO_RAM binary
238  *  \ingroup pico_platform
239  *
240  * For example a `uint32_t` variable explicitly placed in flash (it will hard fault if you attempt to write it!)
241  *
242  *     uint32_t __in_flash("my_group_name") foo = 23;
243  *
244  * The section attribute is `.flashdata.<group>`
245  *
246  * \param group a string suffix to use in the section name to distinguish groups that can be linker
247  *              garbage-collected independently
248  */
249 #define __in_flash(group) __attribute__((section(".flashdata." group)))
250 
251 /*! \brief Indicates a function should not be stored in flash
252  *  \ingroup pico_platform
253  *
254  * Decorates a function name, such that the function will execute from RAM (assuming it is not inlined
255  * into a flash function by the compiler)
256  *
257  * For example a function called my_func taking an int parameter:
258  *
259  *     void __not_in_flash_func(my_func)(int some_arg) {
260  *
261  * The function is placed in the `.time_critical.<func_name>` linker section
262  *
263  * \see __no_inline_not_in_flash_func
264  */
265 #define __not_in_flash_func(func_name) __not_in_flash(__STRING(func_name)) func_name
266 
267 /*! \brief Indicates a function is time/latency critical and should not run from flash
268  *  \ingroup pico_platform
269  *
270  * Decorates a function name, such that the function will execute from RAM (assuming it is not inlined
271  * into a flash function by the compiler) to avoid possible flash latency. Currently this macro is identical
272  * in implementation to `__not_in_flash_func`, however the semantics are distinct and a `__time_critical_func`
273  * may in the future be treated more specially to reduce the overhead when calling such function from a flash
274  * function.
275  *
276  * For example a function called my_func taking an int parameter:
277  *
278  *     void __time_critical(my_func)(int some_arg) {
279  *
280  * The function is placed in the `.time_critical.<func_name>` linker section
281  *
282  * \see __not_in_flash_func
283  */
284 #define __time_critical_func(func_name) __not_in_flash_func(func_name)
285 
286 /*! \brief Indicate a function should not be stored in flash and should not be inlined
287  *  \ingroup pico_platform
288  *
289  * Decorates a function name, such that the function will execute from RAM, explicitly marking it as
290  * noinline to prevent it being inlined into a flash function by the compiler
291  *
292  * For example a function called my_func taking an int parameter:
293  *
294  *     void __no_inline_not_in_flash_func(my_func)(int some_arg) {
295  *
296  * The function is placed in the `.time_critical.<func_name>` linker section
297  */
298 #define __no_inline_not_in_flash_func(func_name) __noinline __not_in_flash_func(func_name)
299 
300 #define __packed_aligned __packed __aligned(4)
301 
302 /*! \brief Attribute to force inlining of a function regardless of optimization level
303  *  \ingroup pico_platform
304  *
305  *  For example my_function here will always be inlined:
306  *
307  *      int __force_inline my_function(int x) {
308  *
309  */
310 
311 // littlekernel: force this on always for the GNU case
312 //#if PICO_C_COMPILER_IS_GNU && (__GNUC__ <= 6 || (__GNUC__ == 7 && (__GNUC_MINOR__ < 3 || !defined(__cplusplus))))
313 #if PICO_C_COMPILER_IS_GNU
314 #define __force_inline inline __always_inline
315 #else
316 #define __force_inline __always_inline
317 #endif
318 
319 /*! \brief Macro to determine the number of elements in an array
320  *  \ingroup pico_platform
321  */
322 #ifndef count_of
323 #define count_of(a) (sizeof(a)/sizeof((a)[0]))
324 #endif
325 
326 /*! \brief Macro to return the maximum of two comparable values
327  *  \ingroup pico_platform
328  */
329 #ifndef MAX
330 #define MAX(a, b) ((a)>(b)?(a):(b))
331 #endif
332 
333 /*! \brief Macro to return the minimum of two comparable values
334  *  \ingroup pico_platform
335  */
336 #ifndef MIN
337 #define MIN(a, b) ((b)>(a)?(a):(b))
338 #endif
339 
340 #define pico_default_asm(...) __asm (".syntax unified\n" __VA_ARGS__)
341 #define pico_default_asm_volatile(...) __asm volatile (".syntax unified\n" __VA_ARGS__)
342 
343 /*! \brief Execute a breakpoint instruction
344  *  \ingroup pico_platform
345  */
__breakpoint(void)346 static inline void __breakpoint(void) {
347     pico_default_asm ("bkpt #0");
348 }
349 
350 /*! \brief Ensure that the compiler does not move memory access across this method call
351  *  \ingroup pico_platform
352  *
353  *  For example in the following code:
354  *
355  *      *some_memory_location = var_a;
356  *      __compiler_memory_barrier();
357  *      uint32_t var_b = *some_other_memory_location
358  *
359  * The compiler will not move the load from `some_other_memory_location` above the memory barrier (which it otherwise
360  * might - even above the memory store!)
361  */
__compiler_memory_barrier(void)362 __force_inline static void __compiler_memory_barrier(void) {
363     pico_default_asm_volatile ("" : : : "memory");
364 }
365 
366 /*! \brief Macro for converting memory addresses to 32 bit addresses suitable for DMA
367  *  \ingroup pico_platform
368  *
369  *  This is just a cast to `uintptr_t` on the RP2040, however you may want to use this when developing code
370  *  that also runs in "host" mode. If the host mode is 64 bit and you are embedding data pointers
371  *  in other data (e.g. DMA chaining), then there is a need in "host" mode to convert a 64 bit native
372  *  pointer to a 32 bit value for storage, which can be done using this macro.
373  */
374 #define host_safe_hw_ptr(x) ((uintptr_t)(x))
375 #define native_safe_hw_ptr(x) host_safe_hw_ptr(x)
376 
377 
378 /*! \brief Panics with the message "Unsupported"
379  *  \ingroup pico_platform
380  *  \see panic
381  */
382 void __attribute__((noreturn)) panic_unsupported(void);
383 
384 /*! \brief Displays a panic message and halts execution
385  *  \ingroup pico_platform
386  *
387  * An attempt is made to output the message to all registered STDOUT drivers
388  * after which this method executes a BKPT instruction.
389  *
390  * @param fmt format string (printf-like)
391  * @param ...  printf-like arguments
392  */
393 void __attribute__((noreturn)) panic(const char *fmt, ...);
394 
395 #ifdef NDEBUG
396 #define panic_compact(...) panic(__VA_ARGS__)
397 #else
398 #define panic_compact(...) panic("")
399 #endif
400 
401 // PICO_CONFIG: PICO_NO_FPGA_CHECK, Remove the FPGA platform check for small code size reduction, type=bool, default=0, advanced=true, group=pico_runtime
402 #ifndef PICO_NO_FPGA_CHECK
403 #define PICO_NO_FPGA_CHECK 0
404 #endif
405 
406 #if PICO_NO_FPGA_CHECK
running_on_fpga(void)407 static inline bool running_on_fpga(void) {return false;}
408 #else
409 bool running_on_fpga(void);
410 #endif
411 
412 /*! \brief Returns the RP2040 chip revision number
413  *  \ingroup pico_platform
414  * @return the RP2040 chip revision number (1 for B0/B1, 2 for B2)
415  */
416 uint8_t rp2040_chip_version(void);
417 
418 /*! \brief Returns the RP2040 rom version number
419  *  \ingroup pico_platform
420  * @return the RP2040 rom version number (1 for RP2040-B0, 2 for RP2040-B1, 3 for RP2040-B2)
421  */
rp2040_rom_version(void)422 static inline uint8_t rp2040_rom_version(void) {
423 GCC_Pragma("GCC diagnostic push")
424 GCC_Pragma("GCC diagnostic ignored \"-Warray-bounds\"")
425     return *(uint8_t*)0x13;
426 GCC_Pragma("GCC diagnostic pop")
427 }
428 
429 /*! \brief No-op function for the body of tight loops
430  *  \ingroup pico_platform
431  *
432  * No-op function intended to be called by any tight hardware polling loop. Using this ubiquitously
433  * makes it much easier to find tight loops, but also in the future \#ifdef-ed support for lockup
434  * debugging might be added
435  */
tight_loop_contents(void)436 static __force_inline void tight_loop_contents(void) {}
437 
438 /*! \brief Multiply two integers using an assembly `MUL` instruction
439  *  \ingroup pico_platform
440  *
441  * This multiplies a by b using multiply instruction using the ARM mul instruction regardless of values (the compiler
442  * might otherwise choose to perform shifts/adds), i.e. this is a 1 cycle operation.
443  *
444  * \param a the first operand
445  * \param b the second operand
446  * \return a * b
447  */
__mul_instruction(int32_t a,int32_t b)448 __force_inline static int32_t __mul_instruction(int32_t a, int32_t b) {
449     pico_default_asm ("muls %0, %1" : "+l" (a) : "l" (b) : );
450     return a;
451 }
452 
453 /*! \brief multiply two integer values using the fastest method possible
454  *  \ingroup pico_platform
455  *
456  * Efficiently multiplies value a by possibly constant value b.
457  *
458  * If b is known to be constant and not zero or a power of 2, then a mul instruction is used rather than gcc's default
459  * which is often a slow combination of shifts and adds. If b is a power of 2 then a single shift is of course preferable
460  * and will be used
461  *
462  * \param a the first operand
463  * \param b the second operand
464  * \return a * b
465  */
466 #define __fast_mul(a, b) __builtin_choose_expr(__builtin_constant_p(b) && !__builtin_constant_p(a), \
467 (__builtin_popcount(b) >= 2 ? __mul_instruction(a,b) : (a)*(b)), \
468 (a)*(b))
469 
470 /*! \brief Utility macro to assert two types are equivalent.
471  *  \ingroup pico_platform
472  *
473  *  This macro can be useful in other macros along with `typeof` to assert that two parameters are of equivalent type
474  *  (or that a single parameter is of an expected type)
475  */
476 #define __check_type_compatible(type_a, type_b) static_assert(__builtin_types_compatible_p(type_a, type_b), __STRING(type_a) " is not compatible with " __STRING(type_b));
477 
478 /*! \brief Get the current exception level on this core
479  *  \ingroup pico_platform
480  *
481  * \return the exception number if the CPU is handling an exception, or 0 otherwise
482  */
__get_current_exception(void)483 static __force_inline uint __get_current_exception(void) {
484     uint exception;
485     pico_default_asm( "mrs %0, ipsr" : "=l" (exception));
486     return exception;
487 }
488 
489 #define WRAPPER_FUNC(x) __wrap_ ## x
490 #define REAL_FUNC(x) __real_ ## x
491 
492 /*! \brief Helper method to busy-wait for at least the given number of cycles
493  *  \ingroup pico_platform
494  *
495  * This method is useful for introducing very short delays.
496  *
497  * This method busy-waits in a tight loop for the given number of system clock cycles. The total wait time is only accurate to within 2 cycles,
498  * and this method uses a loop counter rather than a hardware timer, so the method will always take longer than expected if an
499  * interrupt is handled on the calling core during the busy-wait; you can of course disable interrupts to prevent this.
500  *
501  * You can use \ref clock_get_hz(clk_sys) to determine the number of clock cycles per second if you want to convert an actual
502  * time duration to a number of cycles.
503  *
504  * \param minimum_cycles the minimum number of system clock cycles to delay for
505  */
busy_wait_at_least_cycles(uint32_t minimum_cycles)506 static inline void busy_wait_at_least_cycles(uint32_t minimum_cycles) {
507     pico_default_asm_volatile(
508         "1: subs %0, #3\n"
509         "bcs 1b\n"
510         : "+l" (minimum_cycles) : : "memory"
511     );
512 }
513 
514 /*! \brief Get the current core number
515  *  \ingroup pico_platform
516  *
517  * \return The core number the call was made from
518  */
get_core_num(void)519 __force_inline static uint get_core_num(void) {
520     return (*(uint32_t *) (SIO_BASE + SIO_CPUID_OFFSET));
521 }
522 
523 #ifdef __cplusplus
524 }
525 #endif
526 
527 #else // __ASSEMBLER__
528 
529 #if defined __GNUC__
530 // note LLVM defines __GNUC__
531 #ifdef __clang__
532 #define PICO_ASSEMBLER_IS_CLANG 1
533 #else
534 #define PICO_ASSEMBLER_IS_GNU 1
535 #endif
536 #elif defined __ICCARM__
537 #else
538 #error Unsupported toolchain
539 #endif
540 
541 #define WRAPPER_FUNC_NAME(x) __wrap_##x
542 #define SECTION_NAME(x) .text.##x
543 #define RAM_SECTION_NAME(x) .time_critical.##x
544 
545 #endif // !__ASSEMBLER__
546 
547 #endif
548