From 9ab0cf9683f832cdb48e2099533a5b06a3afcd64 Mon Sep 17 00:00:00 2001 From: An Dao Date: Wed, 13 May 2026 14:56:25 +0200 Subject: Moved tx_initialize_low_level.S to example_build for Cortex-M33/M55/M85 GNU (#452) (#514) The Cortex-M33, M55, and M85 GNU ports incorrectly include tx_initialize_low_level.S as a library source in CMakeLists.txt. This file is board-specific initialization code that users must customize for their hardware, and including it in the library causes linker conflicts when users provide their own implementation (e.g., via CMake FetchContent). This change aligns these ports with the established pattern used by Cortex-M0/M3/M4/M7 GNU ports: - Move tx_initialize_low_level.S from src/ to example_build/ - Remove it from CMakeLists.txt target_sources - Add sample_threadx.c to example_build/ for consistency Signed-off-by: An Dao --- ports/cortex_m33/gnu/CMakeLists.txt | 11 +- .../cortex_m33/gnu/example_build/sample_threadx.c | 370 +++++++++++++++++++++ .../gnu/example_build/tx_initialize_low_level.S | 273 +++++++++++++++ ports/cortex_m33/gnu/src/tx_initialize_low_level.S | 273 --------------- ports/cortex_m55/gnu/CMakeLists.txt | 11 +- .../cortex_m55/gnu/example_build/sample_threadx.c | 370 +++++++++++++++++++++ .../gnu/example_build/tx_initialize_low_level.S | 273 +++++++++++++++ ports/cortex_m55/gnu/src/tx_initialize_low_level.S | 273 --------------- ports/cortex_m85/gnu/CMakeLists.txt | 11 +- .../cortex_m85/gnu/example_build/sample_threadx.c | 370 +++++++++++++++++++++ .../gnu/example_build/tx_initialize_low_level.S | 273 +++++++++++++++ ports/cortex_m85/gnu/src/tx_initialize_low_level.S | 273 --------------- 12 files changed, 1950 insertions(+), 831 deletions(-) create mode 100644 ports/cortex_m33/gnu/example_build/sample_threadx.c create mode 100644 ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S delete mode 100644 ports/cortex_m33/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_m55/gnu/example_build/sample_threadx.c create mode 100644 ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S delete mode 100644 ports/cortex_m55/gnu/src/tx_initialize_low_level.S create mode 100644 ports/cortex_m85/gnu/example_build/sample_threadx.c create mode 100644 ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S delete mode 100644 ports/cortex_m85/gnu/src/tx_initialize_low_level.S diff --git a/ports/cortex_m33/gnu/CMakeLists.txt b/ports/cortex_m33/gnu/CMakeLists.txt index 5ad3b8e7..763a2696 100644 --- a/ports/cortex_m33/gnu/CMakeLists.txt +++ b/ports/cortex_m33/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m33/gnu/example_build/sample_threadx.c b/ports/cortex_m33/gnu/example_build/sample_threadx.c new file mode 100644 index 00000000..13ffadba --- /dev/null +++ b/ports/cortex_m33/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S b/ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 00000000..5915dc96 --- /dev/null +++ b/ports/cortex_m33/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + +/* Setup the stack and heap areas. */ + +STACK_SIZE = 0x00000400 +HEAP_SIZE = 0x00000000 + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M33/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_initialize_low_level + .thumb_func +.type _tx_initialize_low_level, function +_tx_initialize_low_level: + + /* Disable interrupts during ThreadX initialization. */ + CPSID i + + /* Set base of available memory to end of non-initialised RAM area. */ + LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer + LDR r1, =__RAM_segment_used_end__ // Build first free address + ADD r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =_vectors // Pickup address of vector table + STR r1, [r0, #0xD08] // Set vector table address + + /* Enable the cycle count register. */ + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + ORR r1, r1, #1 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Set system stack pointer from vector value. */ + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =_vectors // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Configure SysTick. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] // Setup SysTick Reload Value + MOV r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers + // Note: PnSV must be lowest priority, which is 0xFF + + /* Return to caller. */ + BX lr +// } + + +/* Define shells for each of the unused vectors. */ + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_BadHandler + .thumb_func +.type __tx_BadHandler, function +__tx_BadHandler: + B __tx_BadHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_IntHandler + .thumb_func +.type __tx_IntHandler, function +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + /* Do interrupt handler work here */ + /* .... */ +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global SysTick_Handler + .thumb_func +.type SysTick_Handler, function +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global HardFault_Handler + .thumb_func +.type HardFault_Handler, function +HardFault_Handler: + B HardFault_Handler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global UsageFault_Handler + .thumb_func +.type UsageFault_Handler, function +UsageFault_Handler: + CPSID i // Disable interrupts + // Check for stack limit fault + LDR r0, =0xE000ED28 // CFSR address + LDR r1,[r0] // Pick up CFSR + TST r1, #0x00100000 // Check for Stack Overflow +_unhandled_usage_loop: + BEQ _unhandled_usage_loop // If not stack overflow then loop + + // Handle stack overflow + STR r1, [r0] // Clear CFSR flag(s) + +#ifdef __ARM_FP + LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address + LDR r1, [r0] // Load FPCCR + BIC r1, r1, #1 // Clear the lazy preservation active bit + STR r1, [r0] // Store the value +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r0,[r0] // Pick up current thread pointer + PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) + BL _tx_thread_stack_error_handler // Call ThreadX/user handler + POP {r0,lr} // Restore LR and dummy reg + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + // Call the thread exit function to indicate the thread is no longer executing. + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, lr} // Recover LR +#endif + + MOV r1, #0 // Build NULL value + LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer + STR r1, [r0] // Clear current thread pointer + + // Return from UsageFault_Handler exception + LDR r0, =0xE000ED04 // Load ICSR + LDR r1, =0x10000000 // Set PENDSVSET bit + STR r1, [r0] // Store ICSR + DSB // Wait for memory access to complete + CPSIE i // Enable interrupts + BX lr // Return from exception + + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_NMIHandler + .thumb_func +.type __tx_NMIHandler, function +__tx_NMIHandler: + B __tx_NMIHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_DBGHandler + .thumb_func +.type __tx_DBGHandler, function +__tx_DBGHandler: + B __tx_DBGHandler + + .end diff --git a/ports/cortex_m33/gnu/src/tx_initialize_low_level.S b/ports/cortex_m33/gnu/src/tx_initialize_low_level.S deleted file mode 100644 index 5915dc96..00000000 --- a/ports/cortex_m33/gnu/src/tx_initialize_low_level.S +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE -#include "tx_user.h" -#endif - -SYSTEM_CLOCK = 6000000 -SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) - -/* Setup the stack and heap areas. */ - -STACK_SIZE = 0x00000400 -HEAP_SIZE = 0x00000000 - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-M33/GNU */ -/* 6.2.1 */ -/* AUTHOR */ -/* */ -/* Scott Larson, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for any low-level processor */ -/* initialization, including setting up interrupt vectors, setting */ -/* up a periodic timer interrupt source, saving the system stack */ -/* pointer for use in ISR processing later, and finding the first */ -/* available RAM memory address for tx_application_define. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* */ -/**************************************************************************/ -// VOID _tx_initialize_low_level(VOID) -// { - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global _tx_initialize_low_level - .thumb_func -.type _tx_initialize_low_level, function -_tx_initialize_low_level: - - /* Disable interrupts during ThreadX initialization. */ - CPSID i - - /* Set base of available memory to end of non-initialised RAM area. */ - LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer - LDR r1, =__RAM_segment_used_end__ // Build first free address - ADD r1, r1, #4 // - STR r1, [r0] // Setup first unused memory pointer - - /* Setup Vector Table Offset Register. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =_vectors // Pickup address of vector table - STR r1, [r0, #0xD08] // Set vector table address - - /* Enable the cycle count register. */ - LDR r0, =0xE0001000 // Build address of DWT register - LDR r1, [r0] // Pickup the current value - ORR r1, r1, #1 // Set the CYCCNTENA bit - STR r1, [r0] // Enable the cycle count register - - /* Set system stack pointer from vector value. */ - LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer - LDR r1, =_vectors // Pickup address of vector table - LDR r1, [r1] // Pickup reset stack pointer - STR r1, [r0] // Save system stack pointer - - /* Configure SysTick. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =SYSTICK_CYCLES - STR r1, [r0, #0x14] // Setup SysTick Reload Value - MOV r1, #0x7 // Build SysTick Control Enable Value - STR r1, [r0, #0x10] // Setup SysTick Control - - /* Configure handler priorities. */ - LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM - STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers - LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv - STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers - // Note: SVC must be lowest priority, which is 0xFF - LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM - STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers - // Note: PnSV must be lowest priority, which is 0xFF - - /* Return to caller. */ - BX lr -// } - - -/* Define shells for each of the unused vectors. */ - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_BadHandler - .thumb_func -.type __tx_BadHandler, function -__tx_BadHandler: - B __tx_BadHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_IntHandler - .thumb_func -.type __tx_IntHandler, function -__tx_IntHandler: -// VOID InterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - /* Do interrupt handler work here */ - /* .... */ -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global SysTick_Handler - .thumb_func -.type SysTick_Handler, function -SysTick_Handler: -// VOID TimerInterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - BL _tx_timer_interrupt -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global HardFault_Handler - .thumb_func -.type HardFault_Handler, function -HardFault_Handler: - B HardFault_Handler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global UsageFault_Handler - .thumb_func -.type UsageFault_Handler, function -UsageFault_Handler: - CPSID i // Disable interrupts - // Check for stack limit fault - LDR r0, =0xE000ED28 // CFSR address - LDR r1,[r0] // Pick up CFSR - TST r1, #0x00100000 // Check for Stack Overflow -_unhandled_usage_loop: - BEQ _unhandled_usage_loop // If not stack overflow then loop - - // Handle stack overflow - STR r1, [r0] // Clear CFSR flag(s) - -#ifdef __ARM_FP - LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address - LDR r1, [r0] // Load FPCCR - BIC r1, r1, #1 // Clear the lazy preservation active bit - STR r1, [r0] // Store the value -#endif - - LDR r0, =_tx_thread_current_ptr // Build current thread pointer address - LDR r0,[r0] // Pick up current thread pointer - PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) - BL _tx_thread_stack_error_handler // Call ThreadX/user handler - POP {r0,lr} // Restore LR and dummy reg - -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - // Call the thread exit function to indicate the thread is no longer executing. - PUSH {r0, lr} // Save LR (and r0 just for alignment) - BL _tx_execution_thread_exit // Call the thread exit function - POP {r0, lr} // Recover LR -#endif - - MOV r1, #0 // Build NULL value - LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer - STR r1, [r0] // Clear current thread pointer - - // Return from UsageFault_Handler exception - LDR r0, =0xE000ED04 // Load ICSR - LDR r1, =0x10000000 // Set PENDSVSET bit - STR r1, [r0] // Store ICSR - DSB // Wait for memory access to complete - CPSIE i // Enable interrupts - BX lr // Return from exception - - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_NMIHandler - .thumb_func -.type __tx_NMIHandler, function -__tx_NMIHandler: - B __tx_NMIHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_DBGHandler - .thumb_func -.type __tx_DBGHandler, function -__tx_DBGHandler: - B __tx_DBGHandler - - .end diff --git a/ports/cortex_m55/gnu/CMakeLists.txt b/ports/cortex_m55/gnu/CMakeLists.txt index 5ad3b8e7..763a2696 100644 --- a/ports/cortex_m55/gnu/CMakeLists.txt +++ b/ports/cortex_m55/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m55/gnu/example_build/sample_threadx.c b/ports/cortex_m55/gnu/example_build/sample_threadx.c new file mode 100644 index 00000000..13ffadba --- /dev/null +++ b/ports/cortex_m55/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S b/ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 00000000..ee23d9cb --- /dev/null +++ b/ports/cortex_m55/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + +/* Setup the stack and heap areas. */ + +STACK_SIZE = 0x00000400 +HEAP_SIZE = 0x00000000 + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M55/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_initialize_low_level + .thumb_func +.type _tx_initialize_low_level, function +_tx_initialize_low_level: + + /* Disable interrupts during ThreadX initialization. */ + CPSID i + + /* Set base of available memory to end of non-initialised RAM area. */ + LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer + LDR r1, =__RAM_segment_used_end__ // Build first free address + ADD r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =_vectors // Pickup address of vector table + STR r1, [r0, #0xD08] // Set vector table address + + /* Enable the cycle count register. */ + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + ORR r1, r1, #1 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Set system stack pointer from vector value. */ + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =_vectors // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Configure SysTick. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] // Setup SysTick Reload Value + MOV r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers + // Note: PnSV must be lowest priority, which is 0xFF + + /* Return to caller. */ + BX lr +// } + + +/* Define shells for each of the unused vectors. */ + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_BadHandler + .thumb_func +.type __tx_BadHandler, function +__tx_BadHandler: + B __tx_BadHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_IntHandler + .thumb_func +.type __tx_IntHandler, function +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + /* Do interrupt handler work here */ + /* .... */ +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global SysTick_Handler + .thumb_func +.type SysTick_Handler, function +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global HardFault_Handler + .thumb_func +.type HardFault_Handler, function +HardFault_Handler: + B HardFault_Handler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global UsageFault_Handler + .thumb_func +.type UsageFault_Handler, function +UsageFault_Handler: + CPSID i // Disable interrupts + // Check for stack limit fault + LDR r0, =0xE000ED28 // CFSR address + LDR r1,[r0] // Pick up CFSR + TST r1, #0x00100000 // Check for Stack Overflow +_unhandled_usage_loop: + BEQ _unhandled_usage_loop // If not stack overflow then loop + + // Handle stack overflow + STR r1, [r0] // Clear CFSR flag(s) + +#ifdef __ARM_FP + LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address + LDR r1, [r0] // Load FPCCR + BIC r1, r1, #1 // Clear the lazy preservation active bit + STR r1, [r0] // Store the value +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r0,[r0] // Pick up current thread pointer + PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) + BL _tx_thread_stack_error_handler // Call ThreadX/user handler + POP {r0,lr} // Restore LR and dummy reg + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + // Call the thread exit function to indicate the thread is no longer executing. + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, lr} // Recover LR +#endif + + MOV r1, #0 // Build NULL value + LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer + STR r1, [r0] // Clear current thread pointer + + // Return from UsageFault_Handler exception + LDR r0, =0xE000ED04 // Load ICSR + LDR r1, =0x10000000 // Set PENDSVSET bit + STR r1, [r0] // Store ICSR + DSB // Wait for memory access to complete + CPSIE i // Enable interrupts + BX lr // Return from exception + + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_NMIHandler + .thumb_func +.type __tx_NMIHandler, function +__tx_NMIHandler: + B __tx_NMIHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_DBGHandler + .thumb_func +.type __tx_DBGHandler, function +__tx_DBGHandler: + B __tx_DBGHandler + + .end diff --git a/ports/cortex_m55/gnu/src/tx_initialize_low_level.S b/ports/cortex_m55/gnu/src/tx_initialize_low_level.S deleted file mode 100644 index ee23d9cb..00000000 --- a/ports/cortex_m55/gnu/src/tx_initialize_low_level.S +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE -#include "tx_user.h" -#endif - -SYSTEM_CLOCK = 6000000 -SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) - -/* Setup the stack and heap areas. */ - -STACK_SIZE = 0x00000400 -HEAP_SIZE = 0x00000000 - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-M55/GNU */ -/* 6.2.1 */ -/* AUTHOR */ -/* */ -/* Scott Larson, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for any low-level processor */ -/* initialization, including setting up interrupt vectors, setting */ -/* up a periodic timer interrupt source, saving the system stack */ -/* pointer for use in ISR processing later, and finding the first */ -/* available RAM memory address for tx_application_define. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* */ -/**************************************************************************/ -// VOID _tx_initialize_low_level(VOID) -// { - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global _tx_initialize_low_level - .thumb_func -.type _tx_initialize_low_level, function -_tx_initialize_low_level: - - /* Disable interrupts during ThreadX initialization. */ - CPSID i - - /* Set base of available memory to end of non-initialised RAM area. */ - LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer - LDR r1, =__RAM_segment_used_end__ // Build first free address - ADD r1, r1, #4 // - STR r1, [r0] // Setup first unused memory pointer - - /* Setup Vector Table Offset Register. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =_vectors // Pickup address of vector table - STR r1, [r0, #0xD08] // Set vector table address - - /* Enable the cycle count register. */ - LDR r0, =0xE0001000 // Build address of DWT register - LDR r1, [r0] // Pickup the current value - ORR r1, r1, #1 // Set the CYCCNTENA bit - STR r1, [r0] // Enable the cycle count register - - /* Set system stack pointer from vector value. */ - LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer - LDR r1, =_vectors // Pickup address of vector table - LDR r1, [r1] // Pickup reset stack pointer - STR r1, [r0] // Save system stack pointer - - /* Configure SysTick. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =SYSTICK_CYCLES - STR r1, [r0, #0x14] // Setup SysTick Reload Value - MOV r1, #0x7 // Build SysTick Control Enable Value - STR r1, [r0, #0x10] // Setup SysTick Control - - /* Configure handler priorities. */ - LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM - STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers - LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv - STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers - // Note: SVC must be lowest priority, which is 0xFF - LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM - STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers - // Note: PnSV must be lowest priority, which is 0xFF - - /* Return to caller. */ - BX lr -// } - - -/* Define shells for each of the unused vectors. */ - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_BadHandler - .thumb_func -.type __tx_BadHandler, function -__tx_BadHandler: - B __tx_BadHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_IntHandler - .thumb_func -.type __tx_IntHandler, function -__tx_IntHandler: -// VOID InterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - /* Do interrupt handler work here */ - /* .... */ -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global SysTick_Handler - .thumb_func -.type SysTick_Handler, function -SysTick_Handler: -// VOID TimerInterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - BL _tx_timer_interrupt -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global HardFault_Handler - .thumb_func -.type HardFault_Handler, function -HardFault_Handler: - B HardFault_Handler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global UsageFault_Handler - .thumb_func -.type UsageFault_Handler, function -UsageFault_Handler: - CPSID i // Disable interrupts - // Check for stack limit fault - LDR r0, =0xE000ED28 // CFSR address - LDR r1,[r0] // Pick up CFSR - TST r1, #0x00100000 // Check for Stack Overflow -_unhandled_usage_loop: - BEQ _unhandled_usage_loop // If not stack overflow then loop - - // Handle stack overflow - STR r1, [r0] // Clear CFSR flag(s) - -#ifdef __ARM_FP - LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address - LDR r1, [r0] // Load FPCCR - BIC r1, r1, #1 // Clear the lazy preservation active bit - STR r1, [r0] // Store the value -#endif - - LDR r0, =_tx_thread_current_ptr // Build current thread pointer address - LDR r0,[r0] // Pick up current thread pointer - PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) - BL _tx_thread_stack_error_handler // Call ThreadX/user handler - POP {r0,lr} // Restore LR and dummy reg - -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - // Call the thread exit function to indicate the thread is no longer executing. - PUSH {r0, lr} // Save LR (and r0 just for alignment) - BL _tx_execution_thread_exit // Call the thread exit function - POP {r0, lr} // Recover LR -#endif - - MOV r1, #0 // Build NULL value - LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer - STR r1, [r0] // Clear current thread pointer - - // Return from UsageFault_Handler exception - LDR r0, =0xE000ED04 // Load ICSR - LDR r1, =0x10000000 // Set PENDSVSET bit - STR r1, [r0] // Store ICSR - DSB // Wait for memory access to complete - CPSIE i // Enable interrupts - BX lr // Return from exception - - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_NMIHandler - .thumb_func -.type __tx_NMIHandler, function -__tx_NMIHandler: - B __tx_NMIHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_DBGHandler - .thumb_func -.type __tx_DBGHandler, function -__tx_DBGHandler: - B __tx_DBGHandler - - .end diff --git a/ports/cortex_m85/gnu/CMakeLists.txt b/ports/cortex_m85/gnu/CMakeLists.txt index 5ad3b8e7..763a2696 100644 --- a/ports/cortex_m85/gnu/CMakeLists.txt +++ b/ports/cortex_m85/gnu/CMakeLists.txt @@ -1,7 +1,8 @@ -target_sources(${PROJECT_NAME} PRIVATE +target_sources(${PROJECT_NAME} + PRIVATE + # {{BEGIN_TARGET_SOURCES}} ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_allocate.c ${CMAKE_CURRENT_LIST_DIR}/src/txe_thread_secure_stack_free.c - ${CMAKE_CURRENT_LIST_DIR}/src/tx_initialize_low_level.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_restore.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_context_save.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_interrupt_control.S @@ -14,8 +15,10 @@ target_sources(${PROJECT_NAME} PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_stack_build.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_thread_system_return.S ${CMAKE_CURRENT_LIST_DIR}/src/tx_timer_interrupt.S + # {{END_TARGET_SOURCES}} ) -target_include_directories(${PROJECT_NAME} PUBLIC - inc +target_include_directories(${PROJECT_NAME} + PUBLIC + ${CMAKE_CURRENT_LIST_DIR}/inc ) diff --git a/ports/cortex_m85/gnu/example_build/sample_threadx.c b/ports/cortex_m85/gnu/example_build/sample_threadx.c new file mode 100644 index 00000000..13ffadba --- /dev/null +++ b/ports/cortex_m85/gnu/example_build/sample_threadx.c @@ -0,0 +1,370 @@ +/* This is a small demo of the high-performance ThreadX kernel. It includes examples of eight + threads of different priorities, using a message queue, semaphore, mutex, event flags group, + byte pool, and block pool. */ + +#include "tx_api.h" + +#define DEMO_STACK_SIZE 1024 +#define DEMO_BYTE_POOL_SIZE 9120 +#define DEMO_BLOCK_POOL_SIZE 100 +#define DEMO_QUEUE_SIZE 100 + + +/* Define the ThreadX object control blocks... */ + +TX_THREAD thread_0; +TX_THREAD thread_1; +TX_THREAD thread_2; +TX_THREAD thread_3; +TX_THREAD thread_4; +TX_THREAD thread_5; +TX_THREAD thread_6; +TX_THREAD thread_7; +TX_QUEUE queue_0; +TX_SEMAPHORE semaphore_0; +TX_MUTEX mutex_0; +TX_EVENT_FLAGS_GROUP event_flags_0; +TX_BYTE_POOL byte_pool_0; +TX_BLOCK_POOL block_pool_0; +UCHAR memory_area[DEMO_BYTE_POOL_SIZE]; + + +/* Define the counters used in the demo application... */ + +ULONG thread_0_counter; +ULONG thread_1_counter; +ULONG thread_1_messages_sent; +ULONG thread_2_counter; +ULONG thread_2_messages_received; +ULONG thread_3_counter; +ULONG thread_4_counter; +ULONG thread_5_counter; +ULONG thread_6_counter; +ULONG thread_7_counter; + + +/* Define thread prototypes. */ + +void thread_0_entry(ULONG thread_input); +void thread_1_entry(ULONG thread_input); +void thread_2_entry(ULONG thread_input); +void thread_3_and_4_entry(ULONG thread_input); +void thread_5_entry(ULONG thread_input); +void thread_6_and_7_entry(ULONG thread_input); + + +/* Define main entry point. */ + +int main() +{ + + /* Enter the ThreadX kernel. */ + tx_kernel_enter(); +} + + +/* Define what the initial system looks like. */ + +void tx_application_define(void *first_unused_memory) +{ + +CHAR *pointer = TX_NULL; + + + /* Create a byte memory pool from which to allocate the thread stacks. */ + tx_byte_pool_create(&byte_pool_0, "byte pool 0", memory_area, DEMO_BYTE_POOL_SIZE); + + /* Put system definition stuff in here, e.g. thread creates and other assorted + create information. */ + + /* Allocate the stack for thread 0. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create the main thread. */ + tx_thread_create(&thread_0, "thread 0", thread_0_entry, 0, + pointer, DEMO_STACK_SIZE, + 1, 1, TX_NO_TIME_SLICE, TX_AUTO_START); + + + /* Allocate the stack for thread 1. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 1 and 2. These threads pass information through a ThreadX + message queue. It is also interesting to note that these threads have a time + slice. */ + tx_thread_create(&thread_1, "thread 1", thread_1_entry, 1, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 2. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_2, "thread 2", thread_2_entry, 2, + pointer, DEMO_STACK_SIZE, + 16, 16, 4, TX_AUTO_START); + + /* Allocate the stack for thread 3. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 3 and 4. These threads compete for a ThreadX counting semaphore. + An interesting thing here is that both threads share the same instruction area. */ + tx_thread_create(&thread_3, "thread 3", thread_3_and_4_entry, 3, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 4. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_4, "thread 4", thread_3_and_4_entry, 4, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 5. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create thread 5. This thread simply pends on an event flag which will be set + by thread_0. */ + tx_thread_create(&thread_5, "thread 5", thread_5_entry, 5, + pointer, DEMO_STACK_SIZE, + 4, 4, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 6. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + /* Create threads 6 and 7. These threads compete for a ThreadX mutex. */ + tx_thread_create(&thread_6, "thread 6", thread_6_and_7_entry, 6, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the stack for thread 7. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_STACK_SIZE, TX_NO_WAIT); + + tx_thread_create(&thread_7, "thread 7", thread_6_and_7_entry, 7, + pointer, DEMO_STACK_SIZE, + 8, 8, TX_NO_TIME_SLICE, TX_AUTO_START); + + /* Allocate the message queue. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_QUEUE_SIZE*sizeof(ULONG), TX_NO_WAIT); + + /* Create the message queue shared by threads 1 and 2. */ + tx_queue_create(&queue_0, "queue 0", TX_1_ULONG, pointer, DEMO_QUEUE_SIZE*sizeof(ULONG)); + + /* Create the semaphore used by threads 3 and 4. */ + tx_semaphore_create(&semaphore_0, "semaphore 0", 1); + + /* Create the event flags group used by threads 1 and 5. */ + tx_event_flags_create(&event_flags_0, "event flags 0"); + + /* Create the mutex used by thread 6 and 7 without priority inheritance. */ + tx_mutex_create(&mutex_0, "mutex 0", TX_NO_INHERIT); + + /* Allocate the memory for a small block pool. */ + tx_byte_allocate(&byte_pool_0, (VOID **) &pointer, DEMO_BLOCK_POOL_SIZE, TX_NO_WAIT); + + /* Create a block memory pool to allocate a message buffer from. */ + tx_block_pool_create(&block_pool_0, "block pool 0", sizeof(ULONG), pointer, DEMO_BLOCK_POOL_SIZE); + + /* Allocate a block and release the block memory. */ + tx_block_allocate(&block_pool_0, (VOID **) &pointer, TX_NO_WAIT); + + /* Release the block back to the pool. */ + tx_block_release(pointer); +} + + + +/* Define the test threads. */ + +void thread_0_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sits in while-forever-sleep loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_0_counter++; + + /* Sleep for 10 ticks. */ + tx_thread_sleep(10); + + /* Set event flag 0 to wakeup thread 5. */ + status = tx_event_flags_set(&event_flags_0, 0x1, TX_OR); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_1_entry(ULONG thread_input) +{ + +UINT status; + + + /* This thread simply sends messages to a queue shared by thread 2. */ + while(1) + { + + /* Increment the thread counter. */ + thread_1_counter++; + + /* Send message to queue 0. */ + status = tx_queue_send(&queue_0, &thread_1_messages_sent, TX_WAIT_FOREVER); + + /* Check completion status. */ + if (status != TX_SUCCESS) + break; + + /* Increment the message sent. */ + thread_1_messages_sent++; + } +} + + +void thread_2_entry(ULONG thread_input) +{ + +ULONG received_message; +UINT status; + + /* This thread retrieves messages placed on the queue by thread 1. */ + while(1) + { + + /* Increment the thread counter. */ + thread_2_counter++; + + /* Retrieve a message from the queue. */ + status = tx_queue_receive(&queue_0, &received_message, TX_WAIT_FOREVER); + + /* Check completion status and make sure the message is what we + expected. */ + if ((status != TX_SUCCESS) || (received_message != thread_2_messages_received)) + break; + + /* Otherwise, all is okay. Increment the received message count. */ + thread_2_messages_received++; + } +} + + +void thread_3_and_4_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 3 and thread 4. As the loop + below shows, these function compete for ownership of semaphore_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 3) + thread_3_counter++; + else + thread_4_counter++; + + /* Get the semaphore with suspension. */ + status = tx_semaphore_get(&semaphore_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the semaphore. */ + tx_thread_sleep(2); + + /* Release the semaphore. */ + status = tx_semaphore_put(&semaphore_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} + + +void thread_5_entry(ULONG thread_input) +{ + +UINT status; +ULONG actual_flags; + + + /* This thread simply waits for an event in a forever loop. */ + while(1) + { + + /* Increment the thread counter. */ + thread_5_counter++; + + /* Wait for event flag 0. */ + status = tx_event_flags_get(&event_flags_0, 0x1, TX_OR_CLEAR, + &actual_flags, TX_WAIT_FOREVER); + + /* Check status. */ + if ((status != TX_SUCCESS) || (actual_flags != 0x1)) + break; + } +} + + +void thread_6_and_7_entry(ULONG thread_input) +{ + +UINT status; + + + /* This function is executed from thread 6 and thread 7. As the loop + below shows, these function compete for ownership of mutex_0. */ + while(1) + { + + /* Increment the thread counter. */ + if (thread_input == 6) + thread_6_counter++; + else + thread_7_counter++; + + /* Get the mutex with suspension. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Get the mutex again with suspension. This shows + that an owning thread may retrieve the mutex it + owns multiple times. */ + status = tx_mutex_get(&mutex_0, TX_WAIT_FOREVER); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Sleep for 2 ticks to hold the mutex. */ + tx_thread_sleep(2); + + /* Release the mutex. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + + /* Release the mutex again. This will actually + release ownership since it was obtained twice. */ + status = tx_mutex_put(&mutex_0); + + /* Check status. */ + if (status != TX_SUCCESS) + break; + } +} diff --git a/ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S b/ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S new file mode 100644 index 00000000..22dc0780 --- /dev/null +++ b/ports/cortex_m85/gnu/example_build/tx_initialize_low_level.S @@ -0,0 +1,273 @@ +/*************************************************************************** + * Copyright (c) 2024 Microsoft Corporation + * Copyright (c) 2026-present Eclipse ThreadX contributors + * + * This program and the accompanying materials are made available under the + * terms of the MIT License which is available at + * https://opensource.org/licenses/MIT. + * + * SPDX-License-Identifier: MIT + **************************************************************************/ + + +/**************************************************************************/ +/**************************************************************************/ +/** */ +/** ThreadX Component */ +/** */ +/** Initialize */ +/** */ +/**************************************************************************/ +/**************************************************************************/ + +#ifdef TX_INCLUDE_USER_DEFINE_FILE +#include "tx_user.h" +#endif + +SYSTEM_CLOCK = 6000000 +SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) + +/* Setup the stack and heap areas. */ + +STACK_SIZE = 0x00000400 +HEAP_SIZE = 0x00000000 + +/**************************************************************************/ +/* */ +/* FUNCTION RELEASE */ +/* */ +/* _tx_initialize_low_level Cortex-M85/GNU */ +/* 6.2.1 */ +/* AUTHOR */ +/* */ +/* Scott Larson, Microsoft Corporation */ +/* */ +/* DESCRIPTION */ +/* */ +/* This function is responsible for any low-level processor */ +/* initialization, including setting up interrupt vectors, setting */ +/* up a periodic timer interrupt source, saving the system stack */ +/* pointer for use in ISR processing later, and finding the first */ +/* available RAM memory address for tx_application_define. */ +/* */ +/* INPUT */ +/* */ +/* None */ +/* */ +/* OUTPUT */ +/* */ +/* None */ +/* */ +/* CALLS */ +/* */ +/* None */ +/* */ +/* CALLED BY */ +/* */ +/* _tx_initialize_kernel_enter ThreadX entry function */ +/* */ +/**************************************************************************/ +// VOID _tx_initialize_low_level(VOID) +// { + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global _tx_initialize_low_level + .thumb_func +.type _tx_initialize_low_level, function +_tx_initialize_low_level: + + /* Disable interrupts during ThreadX initialization. */ + CPSID i + + /* Set base of available memory to end of non-initialised RAM area. */ + LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer + LDR r1, =__RAM_segment_used_end__ // Build first free address + ADD r1, r1, #4 // + STR r1, [r0] // Setup first unused memory pointer + + /* Setup Vector Table Offset Register. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =_vectors // Pickup address of vector table + STR r1, [r0, #0xD08] // Set vector table address + + /* Enable the cycle count register. */ + LDR r0, =0xE0001000 // Build address of DWT register + LDR r1, [r0] // Pickup the current value + ORR r1, r1, #1 // Set the CYCCNTENA bit + STR r1, [r0] // Enable the cycle count register + + /* Set system stack pointer from vector value. */ + LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer + LDR r1, =_vectors // Pickup address of vector table + LDR r1, [r1] // Pickup reset stack pointer + STR r1, [r0] // Save system stack pointer + + /* Configure SysTick. */ + MOV r0, #0xE000E000 // Build address of NVIC registers + LDR r1, =SYSTICK_CYCLES + STR r1, [r0, #0x14] // Setup SysTick Reload Value + MOV r1, #0x7 // Build SysTick Control Enable Value + STR r1, [r0, #0x10] // Setup SysTick Control + + /* Configure handler priorities. */ + LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM + STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers + LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv + STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers + // Note: SVC must be lowest priority, which is 0xFF + LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM + STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers + // Note: PnSV must be lowest priority, which is 0xFF + + /* Return to caller. */ + BX lr +// } + + +/* Define shells for each of the unused vectors. */ + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_BadHandler + .thumb_func +.type __tx_BadHandler, function +__tx_BadHandler: + B __tx_BadHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_IntHandler + .thumb_func +.type __tx_IntHandler, function +__tx_IntHandler: +// VOID InterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + /* Do interrupt handler work here */ + /* .... */ +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global SysTick_Handler + .thumb_func +.type SysTick_Handler, function +SysTick_Handler: +// VOID TimerInterruptHandler (VOID) +// { + PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_enter // Call the ISR enter function +#endif + BL _tx_timer_interrupt +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + BL _tx_execution_isr_exit // Call the ISR exit function +#endif + POP {r0,lr} + BX lr +// } + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global HardFault_Handler + .thumb_func +.type HardFault_Handler, function +HardFault_Handler: + B HardFault_Handler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global UsageFault_Handler + .thumb_func +.type UsageFault_Handler, function +UsageFault_Handler: + CPSID i // Disable interrupts + // Check for stack limit fault + LDR r0, =0xE000ED28 // CFSR address + LDR r1,[r0] // Pick up CFSR + TST r1, #0x00100000 // Check for Stack Overflow +_unhandled_usage_loop: + BEQ _unhandled_usage_loop // If not stack overflow then loop + + // Handle stack overflow + STR r1, [r0] // Clear CFSR flag(s) + +#ifdef __ARM_FP + LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address + LDR r1, [r0] // Load FPCCR + BIC r1, r1, #1 // Clear the lazy preservation active bit + STR r1, [r0] // Store the value +#endif + + LDR r0, =_tx_thread_current_ptr // Build current thread pointer address + LDR r0,[r0] // Pick up current thread pointer + PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) + BL _tx_thread_stack_error_handler // Call ThreadX/user handler + POP {r0,lr} // Restore LR and dummy reg + +#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) + // Call the thread exit function to indicate the thread is no longer executing. + PUSH {r0, lr} // Save LR (and r0 just for alignment) + BL _tx_execution_thread_exit // Call the thread exit function + POP {r0, lr} // Recover LR +#endif + + MOV r1, #0 // Build NULL value + LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer + STR r1, [r0] // Clear current thread pointer + + // Return from UsageFault_Handler exception + LDR r0, =0xE000ED04 // Load ICSR + LDR r1, =0x10000000 // Set PENDSVSET bit + STR r1, [r0] // Store ICSR + DSB // Wait for memory access to complete + CPSIE i // Enable interrupts + BX lr // Return from exception + + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_NMIHandler + .thumb_func +.type __tx_NMIHandler, function +__tx_NMIHandler: + B __tx_NMIHandler + + + .section .text + .balign 4 + .syntax unified + .eabi_attribute Tag_ABI_align_preserved, 1 + .global __tx_DBGHandler + .thumb_func +.type __tx_DBGHandler, function +__tx_DBGHandler: + B __tx_DBGHandler + + .end diff --git a/ports/cortex_m85/gnu/src/tx_initialize_low_level.S b/ports/cortex_m85/gnu/src/tx_initialize_low_level.S deleted file mode 100644 index 22dc0780..00000000 --- a/ports/cortex_m85/gnu/src/tx_initialize_low_level.S +++ /dev/null @@ -1,273 +0,0 @@ -/*************************************************************************** - * Copyright (c) 2024 Microsoft Corporation - * Copyright (c) 2026-present Eclipse ThreadX contributors - * - * This program and the accompanying materials are made available under the - * terms of the MIT License which is available at - * https://opensource.org/licenses/MIT. - * - * SPDX-License-Identifier: MIT - **************************************************************************/ - - -/**************************************************************************/ -/**************************************************************************/ -/** */ -/** ThreadX Component */ -/** */ -/** Initialize */ -/** */ -/**************************************************************************/ -/**************************************************************************/ - -#ifdef TX_INCLUDE_USER_DEFINE_FILE -#include "tx_user.h" -#endif - -SYSTEM_CLOCK = 6000000 -SYSTICK_CYCLES = ((SYSTEM_CLOCK / 100) -1) - -/* Setup the stack and heap areas. */ - -STACK_SIZE = 0x00000400 -HEAP_SIZE = 0x00000000 - -/**************************************************************************/ -/* */ -/* FUNCTION RELEASE */ -/* */ -/* _tx_initialize_low_level Cortex-M85/GNU */ -/* 6.2.1 */ -/* AUTHOR */ -/* */ -/* Scott Larson, Microsoft Corporation */ -/* */ -/* DESCRIPTION */ -/* */ -/* This function is responsible for any low-level processor */ -/* initialization, including setting up interrupt vectors, setting */ -/* up a periodic timer interrupt source, saving the system stack */ -/* pointer for use in ISR processing later, and finding the first */ -/* available RAM memory address for tx_application_define. */ -/* */ -/* INPUT */ -/* */ -/* None */ -/* */ -/* OUTPUT */ -/* */ -/* None */ -/* */ -/* CALLS */ -/* */ -/* None */ -/* */ -/* CALLED BY */ -/* */ -/* _tx_initialize_kernel_enter ThreadX entry function */ -/* */ -/**************************************************************************/ -// VOID _tx_initialize_low_level(VOID) -// { - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global _tx_initialize_low_level - .thumb_func -.type _tx_initialize_low_level, function -_tx_initialize_low_level: - - /* Disable interrupts during ThreadX initialization. */ - CPSID i - - /* Set base of available memory to end of non-initialised RAM area. */ - LDR r0, =_tx_initialize_unused_memory // Build address of unused memory pointer - LDR r1, =__RAM_segment_used_end__ // Build first free address - ADD r1, r1, #4 // - STR r1, [r0] // Setup first unused memory pointer - - /* Setup Vector Table Offset Register. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =_vectors // Pickup address of vector table - STR r1, [r0, #0xD08] // Set vector table address - - /* Enable the cycle count register. */ - LDR r0, =0xE0001000 // Build address of DWT register - LDR r1, [r0] // Pickup the current value - ORR r1, r1, #1 // Set the CYCCNTENA bit - STR r1, [r0] // Enable the cycle count register - - /* Set system stack pointer from vector value. */ - LDR r0, =_tx_thread_system_stack_ptr // Build address of system stack pointer - LDR r1, =_vectors // Pickup address of vector table - LDR r1, [r1] // Pickup reset stack pointer - STR r1, [r0] // Save system stack pointer - - /* Configure SysTick. */ - MOV r0, #0xE000E000 // Build address of NVIC registers - LDR r1, =SYSTICK_CYCLES - STR r1, [r0, #0x14] // Setup SysTick Reload Value - MOV r1, #0x7 // Build SysTick Control Enable Value - STR r1, [r0, #0x10] // Setup SysTick Control - - /* Configure handler priorities. */ - LDR r1, =0x00000000 // Rsrv, UsgF, BusF, MemM - STR r1, [r0, #0xD18] // Setup System Handlers 4-7 Priority Registers - LDR r1, =0xFF000000 // SVCl, Rsrv, Rsrv, Rsrv - STR r1, [r0, #0xD1C] // Setup System Handlers 8-11 Priority Registers - // Note: SVC must be lowest priority, which is 0xFF - LDR r1, =0x40FF0000 // SysT, PnSV, Rsrv, DbgM - STR r1, [r0, #0xD20] // Setup System Handlers 12-15 Priority Registers - // Note: PnSV must be lowest priority, which is 0xFF - - /* Return to caller. */ - BX lr -// } - - -/* Define shells for each of the unused vectors. */ - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_BadHandler - .thumb_func -.type __tx_BadHandler, function -__tx_BadHandler: - B __tx_BadHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_IntHandler - .thumb_func -.type __tx_IntHandler, function -__tx_IntHandler: -// VOID InterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - /* Do interrupt handler work here */ - /* .... */ -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global SysTick_Handler - .thumb_func -.type SysTick_Handler, function -SysTick_Handler: -// VOID TimerInterruptHandler (VOID) -// { - PUSH {r0,lr} // Save LR (and dummy r0 to maintain stack alignment) -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_enter // Call the ISR enter function -#endif - BL _tx_timer_interrupt -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - BL _tx_execution_isr_exit // Call the ISR exit function -#endif - POP {r0,lr} - BX lr -// } - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global HardFault_Handler - .thumb_func -.type HardFault_Handler, function -HardFault_Handler: - B HardFault_Handler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global UsageFault_Handler - .thumb_func -.type UsageFault_Handler, function -UsageFault_Handler: - CPSID i // Disable interrupts - // Check for stack limit fault - LDR r0, =0xE000ED28 // CFSR address - LDR r1,[r0] // Pick up CFSR - TST r1, #0x00100000 // Check for Stack Overflow -_unhandled_usage_loop: - BEQ _unhandled_usage_loop // If not stack overflow then loop - - // Handle stack overflow - STR r1, [r0] // Clear CFSR flag(s) - -#ifdef __ARM_FP - LDR r0, =0xE000EF34 // Cleanup FPU context: Load FPCCR address - LDR r1, [r0] // Load FPCCR - BIC r1, r1, #1 // Clear the lazy preservation active bit - STR r1, [r0] // Store the value -#endif - - LDR r0, =_tx_thread_current_ptr // Build current thread pointer address - LDR r0,[r0] // Pick up current thread pointer - PUSH {r0,lr} // Save LR (and r0 to maintain stack alignment) - BL _tx_thread_stack_error_handler // Call ThreadX/user handler - POP {r0,lr} // Restore LR and dummy reg - -#if (defined(TX_ENABLE_EXECUTION_CHANGE_NOTIFY) || defined(TX_EXECUTION_PROFILE_ENABLE)) - // Call the thread exit function to indicate the thread is no longer executing. - PUSH {r0, lr} // Save LR (and r0 just for alignment) - BL _tx_execution_thread_exit // Call the thread exit function - POP {r0, lr} // Recover LR -#endif - - MOV r1, #0 // Build NULL value - LDR r0, =_tx_thread_current_ptr // Pickup address of current thread pointer - STR r1, [r0] // Clear current thread pointer - - // Return from UsageFault_Handler exception - LDR r0, =0xE000ED04 // Load ICSR - LDR r1, =0x10000000 // Set PENDSVSET bit - STR r1, [r0] // Store ICSR - DSB // Wait for memory access to complete - CPSIE i // Enable interrupts - BX lr // Return from exception - - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_NMIHandler - .thumb_func -.type __tx_NMIHandler, function -__tx_NMIHandler: - B __tx_NMIHandler - - - .section .text - .balign 4 - .syntax unified - .eabi_attribute Tag_ABI_align_preserved, 1 - .global __tx_DBGHandler - .thumb_func -.type __tx_DBGHandler, function -__tx_DBGHandler: - B __tx_DBGHandler - - .end -- cgit v1.3.1