text: /*
   * 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>
  
  #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
  
  extern K_THREAD_STACK_DEFINE(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;
  	__DSB();
  	__ISB();
  
  	/* 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);
  
  		__DSB();
  		__ISB();
  
  		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");
  	}
  }
  