| /* |
| * Copyright (c) 2020 Nordic Semiconductor ASA |
| * |
| * SPDX-License-Identifier: Apache-2.0 |
| */ |
| |
| #include <zephyr/kernel.h> |
| #include <zephyr/sys/printk.h> |
| #include <zephyr/sys/__assert.h> |
| #include <zephyr/arch/arm/aarch32/cortex_m/cmsis.h> |
| #include <zephyr/sys/barrier.h> |
| |
| #if !defined(CONFIG_CPU_CORTEX_M) |
| #error test can only run on Cortex-M MCUs |
| #endif |
| |
| #if defined(CONFIG_ARMV8_1_M_MAINLINE) |
| /* |
| * For ARMv8.1-M, the FPSCR[18:16] LTPSIZE field may always read 0b010 if MVE |
| * is not implemented, so mask it when validating the value of the FPSCR. |
| */ |
| #define FPSCR_MASK (~FPU_FPDSCR_LTPSIZE_Msk) |
| #else |
| #define FPSCR_MASK (0xffffffffU) |
| #endif |
| |
| K_THREAD_STACK_DECLARE(z_main_stack, CONFIG_MAIN_STACK_SIZE); |
| |
| static volatile int test_flag; |
| static unsigned int expected_reason; |
| |
| void arm_isr_handler(const void *args) |
| { |
| ARG_UNUSED(args); |
| |
| test_flag++; |
| } |
| |
| void k_sys_fatal_error_handler(unsigned int reason, const z_arch_esf_t *pEsf) |
| { |
| printk("Caught system error -- reason %d\n", reason); |
| |
| if (expected_reason == -1) { |
| printk("Was not expecting a crash\n"); |
| k_fatal_halt(reason); |
| } |
| |
| if (reason != expected_reason) { |
| printk("Wrong crash type got %d expected %d\n", reason, |
| expected_reason); |
| k_fatal_halt(reason); |
| } |
| |
| expected_reason = -1; |
| } |
| |
| void test_main(void) |
| { |
| printk("ARM no-multithreading test\n"); |
| |
| uint32_t psp = (uint32_t)__get_PSP(); |
| uint32_t main_stack_base = (uint32_t)Z_THREAD_STACK_BUFFER(z_main_stack); |
| uint32_t main_stack_top = (uint32_t)(Z_THREAD_STACK_BUFFER(z_main_stack) + |
| K_THREAD_STACK_SIZEOF(z_main_stack)); |
| |
| __ASSERT( |
| (psp >= main_stack_base) && (psp <= main_stack_top), |
| "PSP out of bounds: 0x%x (0x%x - 0x%x)", |
| psp, main_stack_base, main_stack_top); |
| |
| #if defined(CONFIG_FPU) |
| __ASSERT((__get_FPSCR() & FPSCR_MASK) == 0, |
| "FPSCR not zero (0x%x)", __get_FPSCR()); |
| #endif |
| |
| #if defined(CONFIG_BUILTIN_STACK_GUARD) |
| uint32_t psplim = (uint32_t)__get_PSPLIM(); |
| __ASSERT( |
| (psplim == main_stack_base), |
| "PSPLIM not set to main stack base: (0x%x)", |
| psplim); |
| #endif |
| |
| int key = arch_irq_lock(); |
| __ASSERT(arch_irq_unlocked(key), |
| "IRQs locked in main()"); |
| |
| arch_irq_unlock(key); |
| |
| /* Verify activating the PendSV IRQ triggers a K_ERR_SPURIOUS_IRQ */ |
| expected_reason = K_ERR_CPU_EXCEPTION; |
| SCB->ICSR |= SCB_ICSR_PENDSVSET_Msk; |
| barrier_dsync_fence_full(); |
| barrier_isync_fence_full(); |
| |
| /* Determine an NVIC IRQ line that is not currently in use. */ |
| int i, flag = test_flag; |
| |
| __ASSERT(flag == 0, "Test flag not initialized to 0\n"); |
| |
| for (i = CONFIG_NUM_IRQS - 1; i >= 0; i--) { |
| if (NVIC_GetEnableIRQ(i) == 0) { |
| /* |
| * Interrupts configured statically with IRQ_CONNECT(.) |
| * are automatically enabled. NVIC_GetEnableIRQ() |
| * returning false, here, implies that the IRQ line is |
| * either not implemented or it is not enabled, thus, |
| * currently not in use by Zephyr. |
| */ |
| |
| /* Set the NVIC line to pending. */ |
| NVIC_SetPendingIRQ(i); |
| |
| if (NVIC_GetPendingIRQ(i)) { |
| /* If the NVIC line is pending, it is |
| * guaranteed that it is implemented; clear the |
| * line. |
| */ |
| NVIC_ClearPendingIRQ(i); |
| |
| if (!NVIC_GetPendingIRQ(i)) { |
| /* |
| * If the NVIC line can be successfully |
| * un-pended, it is guaranteed that it |
| * can be used for software interrupt |
| * triggering. Trigger it. |
| */ |
| NVIC_SetPendingIRQ(i); |
| break; |
| } |
| } |
| } |
| } |
| |
| if (i >= 0) { |
| |
| printk("Available IRQ line: %u\n", i); |
| |
| arch_irq_connect_dynamic(i, 0 /* highest priority */, |
| arm_isr_handler, |
| NULL, |
| 0); |
| |
| NVIC_EnableIRQ(i); |
| |
| barrier_dsync_fence_full(); |
| barrier_isync_fence_full(); |
| |
| flag = test_flag; |
| |
| __ASSERT(flag > 0, "Test flag not set by IRQ\n"); |
| |
| printk("ARM no multithreading test successful\n"); |
| } else { |
| __ASSERT(0, "No available IRQ line to use in the test\n"); |
| } |
| } |