1 /*
2  * Copyright (c) 2020 Raspberry Pi (Trading) Ltd.
3  *
4  * SPDX-License-Identifier: BSD-3-Clause
5  */
6 
7 /** \file platform.h
8  *  \defgroup pico_platform pico_platform
9  *
10  * \brief Macros and definitions (and functions when included by non assembly code) for the RP2 family device / architecture
11  * to provide a common abstraction over low level compiler / platform specifics
12  *
13  * This header may be included by assembly code
14  */
15 
16 #ifndef _PICO_PLATFORM_H
17 #define _PICO_PLATFORM_H
18 
19 #ifndef _PICO_H
20 #error pico/platform.h should not be included directly; include pico.h instead
21 #endif
22 
23 #include "pico/platform/compiler.h"
24 #include "pico/platform/sections.h"
25 #include "pico/platform/panic.h"
26 #include "hardware/regs/addressmap.h"
27 #include "hardware/regs/sio.h"
28 #ifdef __riscv
29 #include "hardware/regs/rvcsr.h"
30 #endif
31 
32 // PICO_CONFIG: PICO_RP2350A, Whether the current board has an RP2350 in an A (30 GPIO) package, type=bool, default=Usually provided via board header, group=pico_platform
33 #if 0 // make tooling checks happy
34 #define PICO_RP2350A 0
35 #endif
36 
37 // PICO_CONFIG: PICO_RP2350_A2_SUPPORTED, Whether to include any specific software support for RP2350 A2 revision, type=bool, default=1, advanced=true, group=pico_platform
38 #ifndef PICO_RP2350_A2_SUPPORTED
39 #define PICO_RP2350_A2_SUPPORTED 1
40 #endif
41 
42 // PICO_CONFIG: PICO_STACK_SIZE, Minimum amount of stack space reserved in the linker script for each core. See also PICO_CORE1_STACK_SIZE, min=0x100, default=0x800, advanced=true, group=pico_platform
43 #ifndef PICO_STACK_SIZE
44 #define PICO_STACK_SIZE _u(0x800)
45 #endif
46 
47 // PICO_CONFIG: PICO_HEAP_SIZE, Minimum amount of heap space reserved by the linker script, min=0x100, default=0x800, advanced=true, group=pico_platform
48 #ifndef PICO_HEAP_SIZE
49 #define PICO_HEAP_SIZE _u(0x800)
50 #endif
51 
52 // PICO_CONFIG: PICO_NO_RAM_VECTOR_TABLE, Enable/disable the RAM vector table, type=bool, default=0, advanced=true, group=pico_platform
53 #ifndef PICO_NO_RAM_VECTOR_TABLE
54 #define PICO_NO_RAM_VECTOR_TABLE 0
55 #endif
56 
57 #ifndef PICO_RAM_VECTOR_TABLE_SIZE
58 #define PICO_RAM_VECTOR_TABLE_SIZE (VTABLE_FIRST_IRQ + NUM_IRQS)
59 #endif
60 
61 // PICO_CONFIG: PICO_USE_STACK_GUARDS, Enable/disable stack guards, type=bool, default=0, advanced=true, group=pico_platform
62 #ifndef PICO_USE_STACK_GUARDS
63 #define PICO_USE_STACK_GUARDS 0
64 #endif
65 
66 // PICO_CONFIG: PICO_CLKDIV_ROUND_NEAREST, True if floating point clock divisors should be rounded to the nearest possible clock divisor by default rather than rounding down, type=bool, default=1, group=pico_platform
67 #ifndef PICO_CLKDIV_ROUND_NEAREST
68 #define PICO_CLKDIV_ROUND_NEAREST 1
69 #endif
70 
71 #ifndef __ASSEMBLER__
72 
73 /*! \brief No-op function for the body of tight loops
74  *  \ingroup pico_platform
75  *
76  * No-op function intended to be called by any tight hardware polling loop. Using this ubiquitously
77  * makes it much easier to find tight loops, but also in the future \#ifdef-ed support for lockup
78  * debugging might be added
79  */
tight_loop_contents(void)80 static __force_inline void tight_loop_contents(void) {}
81 
82 /*! \brief Helper method to busy-wait for at least the given number of cycles
83  *  \ingroup pico_platform
84  *
85  * This method is useful for introducing very short delays.
86  *
87  * 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,
88  * and this method uses a loop counter rather than a hardware timer, so the method will always take longer than expected if an
89  * interrupt is handled on the calling core during the busy-wait; you can of course disable interrupts to prevent this.
90  *
91  * 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
92  * time duration to a number of cycles.
93  *
94  * \param minimum_cycles the minimum number of system clock cycles to delay for
95  */
busy_wait_at_least_cycles(uint32_t minimum_cycles)96 static inline void busy_wait_at_least_cycles(uint32_t minimum_cycles) {
97     pico_default_asm_volatile (
98 #ifdef __riscv
99             // Note the range is halved on RISC-V due to signed comparison (no carry flag)
100             ".option push\n"
101             ".option norvc\n" // force 32 bit addi, so branch prediction guaranteed
102             ".p2align 2\n"
103             "1: \n"
104             "addi %0, %0, -2 \n"
105             "bgez %0, 1b\n"
106             ".option pop"
107 #else
108     "1: subs %0, #3\n"
109     "bcs 1b\n"
110 #endif
111     : "+r" (minimum_cycles) : : "cc", "memory"
112     );
113 }
114 
115 // PICO_CONFIG: PICO_NO_FPGA_CHECK, Remove the FPGA platform check for small code size reduction, type=bool, default=1, advanced=true, group=pico_runtime
116 #ifndef PICO_NO_FPGA_CHECK
117 #define PICO_NO_FPGA_CHECK 1
118 #endif
119 
120 // PICO_CONFIG: PICO_NO_SIM_CHECK, Remove the SIM platform check for small code size reduction, type=bool, default=1, advanced=true, group=pico_runtime
121 #ifndef PICO_NO_SIM_CHECK
122 #define PICO_NO_SIM_CHECK 1
123 #endif
124 
125 #if PICO_NO_FPGA_CHECK
running_on_fpga(void)126 static inline bool running_on_fpga(void) {return false;}
127 #else
128 bool running_on_fpga(void);
129 #endif
130 #if PICO_NO_SIM_CHECK
running_in_sim(void)131 static inline bool running_in_sim(void) {return false;}
132 #else
133 bool running_in_sim(void);
134 #endif
135 
136 /*! \brief Execute a breakpoint instruction
137  *  \ingroup pico_platform
138  */
__breakpoint(void)139 static __force_inline void __breakpoint(void) {
140 #ifdef __riscv
141     __asm ("ebreak");
142 #else
143     pico_default_asm_volatile ("bkpt #0" : : : "memory");
144 #endif
145 }
146 
147 /*! \brief Get the current core number
148  *  \ingroup pico_platform
149  *
150  * \return The core number the call was made from
151  */
get_core_num(void)152 __force_inline static uint get_core_num(void) {
153     return (*(uint32_t *) (SIO_BASE + SIO_CPUID_OFFSET));
154 }
155 
156 /*! \brief Get the current exception level on this core
157  *  \ingroup pico_platform
158  *
159  * On Cortex-M this is the exception number defined in the architecture
160  * reference, which is equal to VTABLE_FIRST_IRQ + irq num if inside an
161  * interrupt handler. (VTABLE_FIRST_IRQ is defined in platform_defs.h).
162  *
163  * On Hazard3, this function returns VTABLE_FIRST_IRQ + irq num if inside of
164  * an external IRQ handler (or a fault from such a handler), and 0 otherwise,
165  * generally aligning with the Cortex-M values.
166  *
167  * \return the exception number if the CPU is handling an exception, or 0 otherwise
168  */
__get_current_exception(void)169 static __force_inline uint __get_current_exception(void) {
170 #ifdef __riscv
171     uint32_t meicontext;
172     pico_default_asm_volatile (
173             "csrr %0, %1\n"
174     : "=r" (meicontext) : "i" (RVCSR_MEICONTEXT_OFFSET)
175     );
176     if (meicontext & RVCSR_MEICONTEXT_NOIRQ_BITS) {
177         return 0;
178     } else {
179         return VTABLE_FIRST_IRQ + (
180                 (meicontext & RVCSR_MEICONTEXT_IRQ_BITS) >> RVCSR_MEICONTEXT_IRQ_LSB
181         );
182     }
183 #else
184     uint exception;
185     pico_default_asm_volatile (
186         "mrs %0, ipsr\n"
187         "uxtb %0, %0\n"
188         : "=l" (exception)
189     );
190     return exception;
191 #endif
192 }
193 
194 /*! \brief Return true if executing in the NonSecure state (Arm-only)
195  *  \ingroup pico_platform
196  *
197  * \return True if currently executing in the NonSecure state on an Arm processor
198  */
pico_processor_state_is_nonsecure(void)199 __force_inline static bool pico_processor_state_is_nonsecure(void) {
200 #ifndef __riscv
201     // todo add a define to disable NS checking at all?
202     // IDAU-Exempt addresses return S=1 when tested in the Secure state,
203     // whereas executing a tt in the NonSecure state will always return S=0.
204     uint32_t tt;
205     pico_default_asm_volatile (
206         "movs %0, #0\n"
207         "tt %0, %0\n"
208         : "=r" (tt) : : "cc"
209     );
210     return !(tt & (1u << 22));
211 #else
212     // NonSecure is an Arm concept, there is nothing meaningful to return
213     // here. Note it's not possible in general to detect whether you are
214     // executing in U-mode as, for example, M-mode is classically
215     // virtualisable in U-mode.
216     return false;
217 #endif
218 }
219 
220 #define host_safe_hw_ptr(x) ((uintptr_t)(x))
221 #define native_safe_hw_ptr(x) host_safe_hw_ptr(x)
222 
223 /*! \brief Returns the RP2350 chip revision number
224  *  \ingroup pico_platform
225  * @return the RP2350 chip revision number (1 for B0/B1, 2 for B2)
226  */
227 uint8_t rp2350_chip_version(void);
228 
229 /*! \brief Returns the RP2040 chip revision number for compatibility
230  *  \ingroup pico_platform
231  * @return 2 RP2040 errata fixed in B2 are fixed in RP2350
232  */
rp2040_chip_version(void)233 static inline uint8_t rp2040_chip_version(void) {
234     return 2;
235 }
236 
237 /*! \brief Returns the RP2040 rom version number
238  *  \ingroup pico_platform
239  * @return the RP2040 rom version number (1 for RP2040-B0, 2 for RP2040-B1, 3 for RP2040-B2)
240  */
rp2040_rom_version(void)241 static inline uint8_t rp2040_rom_version(void) {
242     GCC_Pragma("GCC diagnostic push")
243     GCC_Pragma("GCC diagnostic ignored \"-Warray-bounds\"")
244     return *(uint8_t*)0x13;
245     GCC_Pragma("GCC diagnostic pop")
246 }
247 
248 /*! \brief Multiply two integers using an assembly `MUL` instruction
249  *  \ingroup pico_platform
250  *
251  * This multiplies a by b using multiply instruction using the ARM mul instruction regardless of values (the compiler
252  * might otherwise choose to perform shifts/adds), i.e. this is a 1 cycle operation.
253  *
254  * \param a the first operand
255  * \param b the second operand
256  * \return a * b
257  */
__mul_instruction(int32_t a,int32_t b)258 __force_inline static int32_t __mul_instruction(int32_t a, int32_t b) {
259 #ifdef __riscv
260     __asm ("mul %0, %0, %1" : "+r" (a) : "r" (b) : );
261 #else
262     pico_default_asm ("muls %0, %1"     : "+l" (a) : "l" (b) : "cc");
263 #endif
264     return a;
265 }
266 
267 /*! \brief multiply two integer values using the fastest method possible
268  *  \ingroup pico_platform
269  *
270  * Efficiently multiplies value a by possibly constant value b.
271  *
272  * 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
273  * 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
274  * and will be used
275  *
276  * \param a the first operand
277  * \param b the second operand
278  * \return a * b
279  */
280 #define __fast_mul(a, b) __builtin_choose_expr(__builtin_constant_p(b) && !__builtin_constant_p(a), \
281     (__builtin_popcount(b) >= 2 ? __mul_instruction(a,b) : (a)*(b)), \
282     (a)*(b))
283 
284 #endif // __ASSEMBLER__
285 
286 #endif
287 
288